I am deep into using the Winforms designer (System.ComponentModel.Design namespace) in my C#/.NET solution so that my users have access to a form designer within my running application. Much of it works well, but I ran into a very specific problem: I encountered a property on a Microsoft control that appears only during design-time--i.e., for the design-time instance of the control. I want to suppress that property so that users cannot modify it when they place an instance of that control on my program's implementation of the Winform design surface.
Details: When a user drag-and-drops a control from the toolbox to the designer surface, I ensure that the newly added designer instance of the control is selected (so that it present resize handles and so the property grid displays that control's design-time properties). I bind the selected objects on the designer surface to the property grid by using the selection service's GetSelectedComponents() method and assigning the property grid's SelectedObjects property to the result.
Many of the controls on my toolbox are .NET controls. One of them is the .NET Winforms TableLayoutPanel control. When you place an instance of that control on a designer surface, you will see a Columns property in the bound PropertyGrid. I would like to suppress that property so that it doesn't appear in the PropertyGrid.
The issue is that this Columns property doesn't appear to exist in the properties list for the TableLayoutPanel type--so using selectedComponents[0].GetType().GetProperties()
, for example, doesn't contain a Columns property. Also, I cannot create a new or override for the existing Columns property because it doesn't appear as an exposed property for the TableLayoutPanel control at design time--thus I cannot decorate it with the Browsable(false)
attribute.
I can't seem to leverage PreFilterProperties
or PostFilterProperties
because I can't interact and customize the TableLayoutPanel's designer.
How can I suppress the Columns designer property for the TableLayoutPanel so that it doesn't appear in the PropertyGrid without having to write my own designer?
If you are trying to avoid writing TableLayoutPanelDesigner
yourself, then here is a workaround that I can suggest.
ITypeDescriptorFilterService
is the interface which is responsible for calling PreFilterProperties
method of the designer. The DesignSurface
class has a an instance of an implementation of this interface registered in its ServiceContainer
. So you can remove the existing registered instance and register a new instance of your own implementation of ITypeDescriptorFilterService
.
In the new implementation, override FilterProperties
and do whatever you think is suitable, for example you can check if the type of the component is TableLayoutPanel
, then don't call its designer PreFilterProperties
.
Then to wrap up the solution, you need to create your own design surface by deriving from DesignSurface
class and removing the registered ITypeDescriptorFilterService
and registering the desired instance which you created.
Example
Just for your information, the name of the property which you are looking for is ColumnStyles
and it has Browsable(false)
attribute by default. But the default designer of TableLayoutPanel
replaces this property with a browsable version.
What I've done in this example is stopping the designer from making those properties Visible.
First provide a custom implementation of ITypeDescriptorFilterService
. The following one is in fact the existing implementation in System.Design
assembly which I've changed its FilterProperties
method and checked if the component is TableLayoutPanel
, I've asked to do nothing.
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
public class TypeDescriptorFilterService : ITypeDescriptorFilterService
{
internal TypeDescriptorFilterService()
{
}
private IDesigner GetDesigner(IComponent component)
{
ISite site = component.Site;
if (site != null)
{
IDesignerHost service = site.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (service != null)
return service.GetDesigner(component);
}
return (IDesigner)null;
}
bool ITypeDescriptorFilterService.FilterAttributes(IComponent component, IDictionary attributes)
{
if (component == null)
throw new ArgumentNullException("component");
if (attributes == null)
throw new ArgumentNullException("attributes");
IDesigner designer = this.GetDesigner(component);
if (designer is IDesignerFilter)
{
((IDesignerFilter)designer).PreFilterAttributes(attributes);
((IDesignerFilter)designer).PostFilterAttributes(attributes);
}
return designer != null;
}
bool ITypeDescriptorFilterService.FilterEvents(IComponent component, IDictionary events)
{
if (component == null)
throw new ArgumentNullException("component");
if (events == null)
throw new ArgumentNullException("events");
IDesigner designer = this.GetDesigner(component);
if (designer is IDesignerFilter)
{
((IDesignerFilter)designer).PreFilterEvents(events);
((IDesignerFilter)designer).PostFilterEvents(events);
}
return designer != null;
}
bool ITypeDescriptorFilterService.FilterProperties(IComponent component, IDictionary properties)
{
if (component == null)
throw new ArgumentNullException("component");
if (properties == null)
throw new ArgumentNullException("properties");
if (typeof(TableLayoutPanel).IsAssignableFrom(component.GetType()))
return true;
IDesigner designer = this.GetDesigner(component);
if (designer is IDesignerFilter)
{
((IDesignerFilter)designer).PreFilterProperties(properties);
((IDesignerFilter)designer).PostFilterProperties(properties);
}
return designer != null;
}
}
Then create a design surface by deriving from DesignSurface
:
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
public class MyDesignSurface : DesignSurface
{
public MyDesignSurface() : base()
{
this.ServiceContainer.RemoveService(typeof(ITypeDescriptorFilterService));
this.ServiceContainer.AddService(typeof(ITypeDescriptorFilterService), new TypeDescriptorFilterService());
}
}
Then for example initialize the design surface this way:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var surface = new MyDesignSurface();
var host = (IDesignerHost)surface.GetService(typeof(IDesignerHost));
var selectionService = (ISelectionService)surface.GetService(typeof(ISelectionService));
surface.BeginLoad(typeof(Form));
var root = (Form)host.RootComponent;
var tableLayoutPanel1 = (Control)host.CreateComponent(typeof(TableLayoutPanel), "tableLayoutPanel1");
root.Controls.Add(tableLayoutPanel1);
var view = (Control)surface.View;
view.Dock = DockStyle.Fill;
this.Controls.Add(view);
selectionService.SetSelectedComponents(new[] { tableLayoutPanel1 });
var propertyGrid1 = new PropertyGrid() { Dock = DockStyle.Right, Width = 200 };
this.Controls.Add(propertyGrid1);
propertyGrid1.SelectedObjects = selectionService.GetSelectedComponents().Cast<object>().ToArray();
}
}
Then run your designer application and you will see Columns
and Rows
properties are hidden as expected.
You need to hide ColumnCount
and RowCount
properties and also the verbs assigned to editing/adding/removing columns and rows.