| File: System\Windows\Forms\ComponentModel\COM2Interop\COM2IVsPerPropertyBrowsingHandler.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 Microsoft.VisualStudio.Shell; using Windows.Win32.System.Com; namespace System.Windows.Forms.ComponentModel.Com2Interop; /// <summary> /// Browsing handler for <see cref="IVsPerPropertyBrowsing"/>. /// </summary> [RequiresUnreferencedCode(ComNativeDescriptor.ComTypeDescriptorsMessage + " Uses Com2IDispatchConverter which is not trim-compatible.")] internal sealed unsafe class Com2IVsPerPropertyBrowsingHandler : Com2ExtendedBrowsingHandler<IVsPerPropertyBrowsing> { public static unsafe bool AllowChildProperties(Com2PropertyDescriptor property) { using var browsing = TryGetComScope(property.TargetObject, out HRESULT hr); if (hr.Succeeded) { BOOL hide = false; hr = browsing.Value->DisplayChildProperties(property.DISPID, &hide); if (hr == HRESULT.S_OK) { return hide; } } return false; } public override void RegisterEvents(Com2PropertyDescriptor[]? properties) { if (properties is null) { return; } for (int i = 0; i < properties.Length; i++) { properties[i].QueryGetDynamicAttributes += OnGetDynamicAttributes; properties[i].QueryGetBaseAttributes += OnGetBaseAttributes; properties[i].QueryGetDisplayName += OnGetDisplayName; properties[i].QueryGetIsReadOnly += OnGetIsReadOnly; properties[i].QueryShouldSerializeValue += OnShouldSerializeValue; properties[i].QueryCanResetValue += OnCanResetPropertyValue; properties[i].QueryResetValue += OnResetPropertyValue; properties[i].QueryGetTypeConverterAndTypeEditor += OnGetTypeConverterAndTypeEditor; } } private void OnGetBaseAttributes(Com2PropertyDescriptor sender, GetAttributesEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { Debug.Assert(sender.TargetObject is null); return; } using BSTR helpString = default; hr = propertyBrowsing.Value->GetLocalizedPropertyInfo(sender.DISPID, PInvokeCore.GetThreadLocale(), null, &helpString); if (hr == HRESULT.S_OK && !helpString.IsNull) { e.Add(new DescriptionAttribute(helpString.ToString())); } } private unsafe void OnGetDynamicAttributes(Com2PropertyDescriptor sender, GetAttributesEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { Debug.Assert(sender.TargetObject is null); return; } hr = HRESULT.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 hide = sender.Attributes[typeof(BrowsableAttribute)] is Attribute browsableAttribute && browsableAttribute.Equals(BrowsableAttribute.No); hr = propertyBrowsing.Value->HideProperty(sender.DISPID, &hide); if (hr == HRESULT.S_OK) { e.Add(hide ? BrowsableAttribute.No : BrowsableAttribute.Yes); } } // Should we show this? if (typeof(IDispatch.Interface).IsAssignableFrom(sender.PropertyType) && sender.CanShow) { BOOL display = false; hr = propertyBrowsing.Value->DisplayChildProperties(sender.DISPID, &display); if (hr == HRESULT.S_OK && display) { e.Add(BrowsableAttribute.Yes); } } } private unsafe void OnCanResetPropertyValue(Com2PropertyDescriptor sender, GetBoolValueEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { Debug.Assert(sender.TargetObject is null); return; } BOOL canReset = e.Value; hr = propertyBrowsing.Value->CanResetPropertyValue(sender.DISPID, &canReset); if (hr.Succeeded) { e.Value = canReset; } } private void OnGetDisplayName(Com2PropertyDescriptor sender, GetNameItemEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { Debug.Assert(sender.TargetObject is null); return; } // Get the localized name, if applicable. using BSTR name = default; hr = propertyBrowsing.Value->GetLocalizedPropertyInfo(sender.DISPID, PInvokeCore.GetThreadLocale(), &name, null); if (hr == HRESULT.S_OK && !name.IsNull) { e.Name = name.ToString(); } } private unsafe void OnGetIsReadOnly(Com2PropertyDescriptor sender, GetBoolValueEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { Debug.Assert(sender.TargetObject is null); return; } // Should we make this read only? BOOL readOnly = false; hr = propertyBrowsing.Value->IsPropertyReadOnly(sender.DISPID, &readOnly); if (hr == HRESULT.S_OK) { e.Value = readOnly; } } private unsafe void OnGetTypeConverterAndTypeEditor(Com2PropertyDescriptor sender, GetTypeConverterAndTypeEditorEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { Debug.Assert(sender.TargetObject is null); return; } // We only do this for IDispatch types. if (sender.CanShow && typeof(IDispatch.Interface).IsAssignableFrom(sender.PropertyType)) { // Should we make this read only? BOOL result; hr = propertyBrowsing.Value->DisplayChildProperties(sender.DISPID, &result); e.TypeConverter = e.TypeConverter is Com2IDispatchConverter ? new Com2IDispatchConverter(sender, hr == HRESULT.S_OK && result) : new Com2IDispatchConverter(hr == HRESULT.S_OK && result, e.TypeConverter); } } private unsafe void OnResetPropertyValue(Com2PropertyDescriptor sender, EventArgs e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { Debug.Assert(sender.TargetObject is null); return; } int dispid = sender.DISPID; BOOL canReset = false; hr = propertyBrowsing.Value->CanResetPropertyValue(dispid, &canReset); if (hr.Succeeded) { propertyBrowsing.Value->ResetPropertyValue(dispid); } } private unsafe void OnShouldSerializeValue(Com2PropertyDescriptor sender, GetBoolValueEvent e) { using var propertyBrowsing = TryGetComScope(sender.TargetObject, out HRESULT hr); if (hr.Failed) { Debug.Assert(sender.TargetObject is null); return; } // By default we say it's default. BOOL isDefault = true; hr = propertyBrowsing.Value->HasDefaultValue(sender.DISPID, &isDefault); if (hr == HRESULT.S_OK && !isDefault) { // Specify a default value editor. e.Value = true; } } }