PropertyGrid Adding EventsTab

I am having problems adding the EventsTab to the propertygrid. I have not been able to find much information on how this is done and documentation is very rare.

My code adds the EventsTab to the propertygrid but it never gets displayed. I am setting the Site of the propertygrid to that of my designer as I have seen that you should do this but still It doesn't display the EventsTab.

Any help would be much appreciated.

Thanks

[453 byte] By [Giftednewt] at [2007-12-23]
# 1
Would you be so kind as to share some of this code so that we can better know what you are doing?
BrendanGrant at 2007-8-30 > top of Msdn Tech,Windows Forms,Windows Forms Designer...
# 2

I have solved this now. It turned out that the site for the property grid was being set again within another function. I removed this call and now the EventsTab is showing.

Giftednewt at 2007-8-30 > top of Msdn Tech,Windows Forms,Windows Forms Designer...
# 3

Hi Giftednewt,

I'm trying to build my own form designer, and I have the same problem than you with the EventsTab, but I can't solve it.

Can you post some code explaining how to do it?

Thank you in advance.

intrus0 at 2007-8-30 > top of Msdn Tech,Windows Forms,Windows Forms Designer...
# 4

This URL might offer some help:

http://www.dotnet247.com/247reference/msgs/55/278312.aspx

Though you might want to paste the code into Visual Studio to get rid of the highlighting and lack of indentation. I'm pretty sure that code just shows you how to put the tab on the property grid. To actually populate it is another story, one which I'm looking into doing at the moment. Good luck!

Quirk at 2007-8-30 > top of Msdn Tech,Windows Forms,Windows Forms Designer...
# 5

I found that setting the site of the propertygrid to that of the selected component solved the problem i was having.

as shown in the code below

I created my own property grid control based off of the propertygrid class as follows :

using System;
using System.Windows.Forms.Design;
using System.Windows.Forms.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
using System.ComponentModel;
using Designer.Designer;

namespace Designer.Design
{
internal class MyPropertyGrid : System.Windows.Forms.PropertyGrid
{
private System.ComponentModel.Container components = null;

public MyPropertyGrid()
{
InitializeComponent();
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}

#region Component Designer generated code
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion

public void ShowEvents(bool show)
{
ShowEventsButton(show);
}

public bool DrawFlat
{
get { return DrawFlatToolbar; }
set { DrawFlatToolbar = value; }
}

protected override void OnPropertyValueChanged (PropertyValueChangedEventArgs e)
{
if (e.ChangedItem.Value == null)
{
((DesignSite)((Component)this.SelectedObject).Site).Events.Remove(e.OldValue);
}
}

}

}

I hooked the OnPropertyGridSeletedObjectChanged Event in which i call a function to update the site of the property grid.

protected void OnPropertyGridSelectedObjectChanged(object sender, EventArgs e)
{
UpdatePropertyGridSite();
this.propertyGrid1.ShowEvents(true);
}

UpdatePropertyGridSite gets the site from the selectedobject that the property grid is showing the properties for. Updates the propertygrids site to that of the selectedobject then adds the tab. This then made the tab visible

protected void UpdatePropertyGridSite()
{
if (propertyGrid1 != null)
{
propertyGrid1.Site = null;
IServiceProvider provider = GetPropertyGridServiceProvider();
if (provider != null)
{
propertyGrid1.Site = (DesignSite)provider;
propertyGrid1.PropertyTabs.AddTabType(typeof(System.Windows.Forms.Design.EventsTab));
}
}
}

protected IServiceProvider GetPropertyGridServiceProvider()
{
object selObject = null;
if (propertyGrid1.SelectedObjects != null && propertyGrid1.SelectedObjects.Length > 0)
selObject = propertyGrid1.SelectedObjects[0];
else
selObject = propertyGrid1.SelectedObject;

if (selObject is Component)
return (selObject as Component).Site;

return null;
}

For showing the events in the events tab I created an EventBindingService that inherits System.ComponentModel.Design.EventBindingService. The following is how i implemented that class

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using Designer.Design;
using System.Reflection;

namespace Designer
{
public class EventBindingService : System.ComponentModel.Design.EventBindingService
{
private Designer.DesignerHost m_host;

public EventBindingService(IServiceProvider myhost) :base(myhost)
{
m_host = (Designer.DesignerHost)myhost;
}

#region IEventBindingService Members

protected override string CreateUniqueMethodName(IComponent component, EventDescriptor e)
{
throw new Exception("The method or operation is not implemented.");
}

protected override System.Collections.ICollection GetCompatibleMethods(System.ComponentModel.EventDescriptor e)
{
throw new Exception("The method or operation is not implemented.");
}

protected override bool ShowCode(System.ComponentModel.IComponent component, System.ComponentModel.EventDescriptor e, string methodName)
{
throw new Exception("The method or operation is not implemented.");
}

protected override bool ShowCode(int lineNumber)
{
throw new Exception("The method or operation is not implemented.");
}

protected override bool ShowCode()
{
throw new Exception("The method or operation is not implemented.");
}

#endregion

}
}

Then what i did was add my eventbindingservice to my service container which holds all the services for the design surface

m_serviceContainer.AddService(typeof(IEventBindingService), new Designer.EventBindingService(m_host));

I then hooked up to the OnSelectionChanged Event of the PropertyGrid. As long as there is only 1 component selected on my design surface we request the eventbindingservice from the site of the design surface.

private void OnSelectionChanged(object sender, System.EventArgs e)
{
ISelectionService s = (ISelectionService)m_serviceContainer.GetService(typeof(ISelectionService));

object[] selection;
if (s.SelectionCount == 0)
propertyGrid1.SelectedObject = null;
else
{
selection = new object[s.SelectionCount];
s.GetSelectedComponents().CopyTo(selection, 0);
propertyGrid1.SelectedObjects = selection;

if (selection.Length == 1)
{
m_EventBindingService = objSurface.Site.GetService(typeof(IEventBindingService)) as Designer.Designer.EventBindingService;
}
}
}

Therefore when the propertygrid interrogates the eventbinding service it requests the events from my eventbinding service. If you wanted you could override the method which returns the collection of events to the propertygrid allowing you to filter what events the user sees

It took me a very long time to figure out what I needed to do to get this working as there is very limited documentation. I'm not saying this is the best way to go about doing this either, but it got it working for me.

Hope this has been of some help

Giftednewt

Giftednewt at 2007-8-30 > top of Msdn Tech,Windows Forms,Windows Forms Designer...
# 6

Thank you very much for your help, Giftednewt and Quirk. The truth is that I don't understand this process very well (I don't known where I have to add the OnSelectionChanged event to filter the events, for example), but anyway, I have a project with a Form that includes a designer (a Form and a Button) and other Form with one PropertyGrid that shows the properties and events of both controls. I'm sure that the project has many bugs, but it could be a good start point for somebody who, like me, doesn't have this process very clear.

Below, is the source code of the unit, if someone is interested or can help him.

If there is a person who knows how this mechanism works... like Giftednewt.... and has a bit of time, I'm pleased that he can comments the source code and clean the code from bugs, adding or deleting whatever he want.

Thank you again and here is the code.


using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel.Design;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;

namespace WindowsFormsDesigner
{
#region MainForm
class MainForm : Form
{
#region Windows Designer
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#endregion

PropertiesForm propertiesForm;
MyDesignSurface designSurface;

public MainForm()
{
this.SuspendLayout();
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(500, 500);
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Windows Forms Designer";
this.ResumeLayout(false);

designSurface = new MyDesignSurface();

propertiesForm = new PropertiesForm(designSurface);
propertiesForm.Left = this.Width + this.Left;
propertiesForm.Height = this.Height;
propertiesForm.Show();

IServiceContainer serviceContainer = (IServiceContainer)designSurface.GetService(typeof(IServiceContainer));
serviceContainer.AddService(typeof(IEventBindingService), new EventBindingService(designSurface));

ISelectionService selectionService = (ISelectionService)designSurface.GetService(typeof(ISelectionService));
selectionService.SelectionChanged += new EventHandler(OnSelectionChanged);

designSurface.BeginLoad(typeof(Form));
Control c = designSurface.View as Control;
c.Parent = this;
c.Dock = DockStyle.Fill;

IDesignerHost designerHost = (IDesignerHost)designSurface.GetService(typeof(IDesignerHost));
System.Drawing.Design.IToolboxUser itu = (System.Drawing.Design.IToolboxUser)designerHost.GetDesigner(designerHost.RootComponent);
itu.ToolPicked(new System.Drawing.Design.ToolboxItem(typeof(Button)));
}

private void OnSelectionChanged(object sender, System.EventArgs e)
{
ISelectionService s = (ISelectionService)designSurface.GetService(typeof(ISelectionService));
IDesignerHost designerHost = (IDesignerHost)designSurface.GetService(typeof(IDesignerHost));

object[] selection;
if (s.SelectionCount == 0)
propertiesForm.SetObjectToPropertyGrid(null);
else
{
selection = new object[s.SelectionCount];
s.GetSelectedComponents().CopyTo(selection, 0);
propertiesForm.SetObjectToPropertyGrid(selection);
}
}
}
#endregion

#region MyDesignerSurface
class MyDesignSurface : DesignSurface, IServiceProvider
{
#region IServiceProvider Members
object IServiceProvider.GetService(Type serviceType)
{
return this.GetService(serviceType);
}
#endregion
}
#endregion

#region PropertiesForm
class PropertiesForm : Form
{
private MyDesignSurface designSurface;
private System.ComponentModel.IContainer components = null;
private MyPropertyGrid pg = null;

protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

public PropertiesForm(MyDesignSurface designSurface)
{
this.designSurface = designSurface;

this.SuspendLayout();

this.TopMost = true;
this.StartPosition = FormStartPosition.Manual;
this.Size = new Size(258, 700);
this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
this.ShowInTaskbar = false;

pg = new MyPropertyGrid();
pg.Parent = this;
pg.Dock = DockStyle.Fill;

this.ResumeLayout(false);
}

internal void SetObjectToPropertyGrid(object[] c)
{
IDesignerHost designerHost = (IDesignerHost)designSurface.GetService(typeof(IDesignerHost));

if (c == null)
pg.SelectedObject = null;
else
pg.SelectedObjects = c;

if (designerHost != null)
{
pg.Site = (new IDEContainer(designerHost)).CreateSite(pg);
pg.PropertyTabs.AddTabType(typeof(System.Windows.Forms.Design.EventsTab), PropertyTabScope.Document);
pg.ShowEvents(true);
}
else
{
pg.Site = null;
}
}
}
#endregion

#region MyPropertyGrid
internal class MyPropertyGrid : System.Windows.Forms.PropertyGrid
{
private System.ComponentModel.Container components = null;

public MyPropertyGrid()
{
InitializeComponent();
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}

#region Component Designer generated code
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion

public void ShowEvents(bool show)
{
ShowEventsButton(show);
}
}
#endregion

#region Site
// this is taken from SharpDevelop. It's a copy&paste
class IDEContainer : Container
{
class IDESite : ISite
{
private string name = "";
private IComponent component;
private IDEContainer container;

public IDESite(IComponent sitedComponent, IDEContainer site, string aName)
{
component = sitedComponent;
container = site;
name = aName;
}

public IComponent Component
{
get { return component; }
}
public IContainer Container
{
get { return container; }
}

public bool DesignMode
{
get { return false; }
}

public string Name
{
get { return name; }
set { name = value; }
}

public object GetService(Type serviceType)
{
return container.GetService(serviceType);
}
}

public IDEContainer(IServiceProvider sp)
{
serviceProvider = sp;
}

protected override object GetService(Type serviceType)
{
object service = base.GetService(serviceType);
if (service == null)
{
service = serviceProvider.GetService(serviceType);
}
return service;
}

public ISite CreateSite(IComponent component)
{
return CreateSite(component, "UNKNOWN_SITE");
}

protected override ISite CreateSite(IComponent component, string name)
{
ISite site = base.CreateSite(component, name);
if (site == null)
{
}
return new IDESite(component, this, name);
}

private IServiceProvider serviceProvider;
}
#endregion

#region EventBindingService
public class EventBindingService : System.ComponentModel.Design.EventBindingService
{
public EventBindingService(IServiceProvider myhost)
: base(myhost)
{
}

#region IEventBindingService Members
protected override string CreateUniqueMethodName(IComponent component, EventDescriptor e)
{
throw new Exception("The method or operation is not implemented.");
}

protected override System.Collections.ICollection GetCompatibleMethods(System.ComponentModel.EventDescriptor e)
{
List<object> l = new List<object>();
return l;
}

protected override bool ShowCode(System.ComponentModel.IComponent component, System.ComponentModel.EventDescriptor e, string methodName)
{
throw new Exception("The method or operation is not implemented.");
}

protected override bool ShowCode(int lineNumber)
{
throw new Exception("The method or operation is not implemented.");
}

protected override bool ShowCode()
{
throw new Exception("The method or operation is not implemented.");
}
#endregion
}
#endregion
}

intrus0 at 2007-8-30 > top of Msdn Tech,Windows Forms,Windows Forms Designer...
# 7
intrus0 wrote:

Thank you very much for your help, Giftednewt and Quirk. The truth is that I don't understand this process very well (I don't known where I have to add the OnSelectionChanged event to filter the events, for example),

I couldn't quite figure that out either, though I did finally get the tab to be populated, so a big thanks to GiftedNewt (I was just missing the EventBindingService...doh).

The OnSelectionChanged that GiftedNewt is referring to, I put in my DesignSurface class since I was already updating the property grid there with that same function. What I don't understand is the "if" check for one component at the end. All it does is set a member variable. How is that variable used with respect to the grid?

Quirk at 2007-8-30 > top of Msdn Tech,Windows Forms,Windows Forms Designer...
# 8

Hi Giftednewt,

Thanks a lot. Can you please add the structure of DesignSite and type of m_serviceContainer and objSurface. I have the similar kind of requirement. I am not able to bring custom control's events collection in form design mode.

Thanks in advance.

Pals.

PALANISELVAM at 2007-8-30 > top of Msdn Tech,Windows Forms,Windows Forms Designer...