| File: System\Windows\Forms\ComponentModel\COM2Interop\COM2IPerPropertyBrowsingHandler.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Drawing.Design; using Windows.Win32.System.Ole; namespace System.Windows.Forms.ComponentModel.Com2Interop; /// <summary> /// Browsing handler for <see cref="IPerPropertyBrowsing"/>. /// </summary> [RequiresUnreferencedCode(ComNativeDescriptor.ComTypeDescriptorsMessage + " Uses COM2PropertyDescriptor which is not trim-compatible.")] internal sealed unsafe partial class Com2IPerPropertyBrowsingHandler : Com2ExtendedBrowsingHandler<IPerPropertyBrowsing> { public override void RegisterEvents(Com2PropertyDescriptor[]? properties) { if (properties is null) { return; } for (int i = 0; i < properties.Length; i++) { properties[i].QueryGetBaseAttributes += OnGetBaseAttributes; properties[i].QueryGetDisplayValue += OnGetDisplayValue; properties[i].QueryGetTypeConverterAndTypeEditor += OnGetTypeConverterAndTypeEditor; } } private static Guid GetPropertyPageGuid(IPerPropertyBrowsing* propertyBrowsing, int dispid) { // Check for a property page Guid guid = Guid.Empty; HRESULT hr = propertyBrowsing->MapPropertyToPage(dispid, &guid); return hr.Succeeded ? guid : Guid.Empty; } internal static bool TryGetDisplayString( IPerPropertyBrowsing* propertyBrowsing, int dispid, [NotNullWhen(true)] out string? displayString) { using BSTR value = default; if (propertyBrowsing->GetDisplayString(dispid, &value).Failed) { displayString = null; return false; } displayString = value.ToString(); return displayString is not null; } /// <summary> /// Here is where we handle IVsPerPropertyBrowsing.GetLocalizedPropertyInfo and IVsPerPropertyBrowsing. We /// hide properties such as IPerPropertyBrowsing, IProvidePropertyBuilder, etc. /// </summary> private void OnGetBaseAttributes(Com2PropertyDescriptor sender, GetAttributesEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Succeeded) { // We hide IDispatch props by default, we we need to force showing them here. bool validPropPage = !Guid.Empty.Equals(GetPropertyPageGuid(propertyBrowsing, sender.DISPID)); if (sender.CanShow && validPropPage) { e.Add(BrowsableAttribute.Yes); } } } private void OnGetDisplayValue(Com2PropertyDescriptor sender, GetNameItemEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { return; } // 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; } if (TryGetDisplayString(propertyBrowsing, sender.DISPID, out string? displayString)) { e.Name = displayString; } } private unsafe void OnGetTypeConverterAndTypeEditor(Com2PropertyDescriptor sender, GetTypeConverterAndTypeEditorEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { return; } // Check for enums. CALPOLESTR caStrings = default; CADWORD caCookies = default; hr = propertyBrowsing.Value->GetPredefinedStrings(sender.DISPID, &caStrings, &caCookies); if (hr.Failed) { Debug.Fail($"IPerPropertyBrowsing::GetPredefinedStrings(dispid={sender.DISPID}) failed."); } // 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 (e.TypeConverter is Com2IPerPropertyEnumConverter) { e.TypeConverter = null; } if (hr == HRESULT.S_OK) { string?[] names = caStrings.ConvertAndFree(); uint[] cookies = caCookies.ConvertAndFree(); if (names.Length > 0 && cookies.Length > 0) { e.TypeConverter = new Com2IPerPropertyEnumConverter(new Com2IPerPropertyBrowsingEnum(sender, names, cookies)); } } else { // If we didn't get any strings, try the property page editor. // // 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 guid = GetPropertyPageGuid(propertyBrowsing, sender.DISPID); if (!Guid.Empty.Equals(guid)) { e.TypeEditor = new Com2PropertyPageUITypeEditor(sender, guid, (UITypeEditor?)e.TypeEditor); } } } }