dot net objects - IDispatch - and the Visual Studio Property Browser

I have some questions about the new MPF framework classes in Visual Studio 2005.

I have implemented the ISelectionContainer interface in my EditorPane.cs.

When the editor detects the user has selected a shape in our drawing -> All I do is in GetObjects() is set apUnkObjects to my dot net objects.

Somehow, the property browser is smart enough to automatically display my object correctly in the property browser. (I imagine it is auto-magically generating an IDispatch interface behind my back).

Is there a way to tell the property browser what the categories are for my properties?

Is there a way to change the name displayed in the ObjectList? (currently it shows my fully qualified type name. I’d rather it display the instance name of the object selected).

Is there a way to make a comment appear in the Field Descriptions pane at the bottom of the property window?

I’m hoping there are some secret attributes I can use to tag the properties/methods of my dot net object.

I tried to derive my dot net object from ICategorizeProperties, but then I realized this interface refers to the dispIds, which I have no access to.

Best regards,
Daren.

public int GetObjects(uint dwFlags, uint cObjects, object[] apUnkObjects)

{

ShellConstants flags = (ShellConstants)dwFlags;

switch (flags)

{

case ShellConstants.GETOBJS_ALL:

// fall through, same in both cases

case ShellConstants.GETOBJS_SELECTED:

{

for (int i = 0; i < this.selectedObjects.Count; i++)

{

apUnkObjectsIdea = this.selectedObjectsIdea;

} // next selected object

}

break;

default:

return VSConstants.E_INVALIDARG;

} // end switch

return VSConstants.S_OK;

}

#region ICategorizeProperties Members

public int GetCategoryName(int PROPCAT, uint lcid, out string pbstrName) // UNSURE HOW TO USE THIS.

{

throw new Exception("The method or operation is not implemented.");

}

public int MapPropertyToCategory(int dispid, out int ppropcat) // I DON’T KNOW THE DISPIDs!

{

throw new Exception("The method or operation is not implemented.");

}

#endregion

} // end class

} // end namespace

[2499 byte] By [DarenYong] at [2008-3-1]
# 1
If you are using the MPF you should not implement ISelectionContainer directly, instead you should use the SelectionContainer object. In order to get category and description you can define .net attributes to the properties in you object.
For example:

public class Test

{

[System.ComponentModel.Description("This is a demo"), System.ComponentModel.Category("This is my category")]

public string A

{

get { return "A"; }

set { }

}

}

When you put this an object instance of this class in the property grid, the category and description is showed for the A property.
You should use SelectionContainer like this:

selectionContainer = new SelectionContainer();
...

ITrackSelection track = GetService<ITrackSelection>();

if (null != track)

{

selectionContainer.SelectedObjects = selectedObjects;

selectionContainer.SelectableObjects = selectedObjects;

track.OnSelectChange((ISelectionContainer)selectionContainer);

}
...

In this sample GetService is a template method

public T GetService<T>()

{

return (T)GetService(typeof(T));

}

Gaston at 2007-9-7 > top of Msdn Tech,Visual Studio,Visual Studio Extensibility...
# 2

Thanks Gaston,

I removed my implementation of ISelectionContainer from my EditorPane.cs (and saved many lines of ugly C++ like COM code!).

One other challenge I'm having is getting an events tab to appear for my object.

I added the attribute:
[PropertyTabAttribute(typeof(WorkflowObjectPropertyTab), PropertyTabScope.Document)]
to my property adapter class.

And created a new class from the MSDN sample (see below).

But when I click on an object (I can see properties), but I dont see a custom property tab, and none of the methods in the PropertyTab class are ever called (I put breakpoints on them).

Any idea whats going wrong? I imagine my PropertyTabAttribute is not being interpreted correctly in VSIP or at all?

Best regards,
D.
using System;
using System.Drawing;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace Captaris.Workflow.Designer.Drawing.Workflow
{
class WorkflowObjectPropertyTab : System.Windows.Forms.Design.EventsTab
{
[BrowsableAttribute(true)]
// This string contains a Base-64 encoded and serialized example property tab image.
private string img = "AAEAAAD/////AQAAAAAAAAAMAgAAAFRTeXN0ZW0uRHJhd2luZywgVmVyc2lvbj0xLjAuMzMwMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWIwM2Y1ZjdmMTFkNTBhM2EFAQAAABVTeXN0ZW0uRHJhd2luZy5CaXRtYXABAAAABERhdGEHAgIAAAAJAwAAAA8DAAAAtgIAAAJCTbYCAAAAAAAANgAAACgAAAANAAAAEAAAAAEAGAAAAAAAAAAAAMQOAADEDgAAAAAAAAAAAADO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tn/ztbZztbZHh4eHh4eztbZztbZztbZztbZztbZztbZztbZztbZztbZ/87W2c7W2QDBAB4eHh4eHs7W2c7W2c7W2c7W2c7W2c7W2c7W2c7W2f/O1tnO1tnO1tkAwQAeHh4eHh7O1tnO1tnO1tnO1tnO1tnO1tnO1tn/ztbZztbZlJSU////AMEAHh4eHh4eztbZztbZztbZztbZztbZztbZ/87W2c7W2c7W2ZSUlP///wDBAB4eHh4eHs7W2c7W2c7W2c7W2c7W2f/O1tnO1tnO1tnO1tmUlJT///8AwQAeHh4eHh7O1tnO1tnO1tnO1tn/ztbZHh4eHh4eHh4eHh4eHh4e////AIAAHh4eHh4eztbZztbZztbZ/87W2ZSUlP///wDBAADBAADBAADBAADBAACAAB4eHh4eHs7W2c7W2f/O1tnO1tmUlJT///8AwQAAgAAeHh4eHh7O1tnO1tnO1tnO1tnO1tn/ztbZztbZztbZlJSU////AMEAAIAAHh4eHh4eztbZztbZztbZztbZ/87W2c7W2c7W2c7W2ZSUlP///wDBAACAAB4eHh4eHs7W2c7W2c7W2f/O1tnO1tnO1tnO1tnO1tmUlJT///8AwQAAgAAeHh4eHh7O1tnO1tn/ztbZztbZztbZztbZztbZztbZlJSU////AMEAAIAAHh4eHh4eztbZ/87W2c7W2c7W2c7W2c7W2c7W2c7W2ZSUlP///wDBAACAAB4eHs7W2f/O1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tnO1tn/Cw==";
private IServiceProvider sp;

public WorkflowObjectPropertyTab(IServiceProvider sp) : base(sp)
{
this.sp = sp;
}

// Returns the properties of the specified component extended with
// a CategoryAttribute reflecting the name of the type of the property.
public override System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes)
{
PropertyDescriptorCollection props;
if (attributes == null)
props = TypeDescriptor.GetProperties(component);
else
props = TypeDescriptor.GetProperties(component, attributes);

PropertyDescriptor[] propArray = new PropertyDescriptor[props.Count];
for (int i = 0; i < props.Count; i++)
{
// Create a new PropertyDescriptor from the old one, with
// a CategoryAttribute matching the name of the type.
propArrayIdea = TypeDescriptor.CreateProperty(propsIdea.ComponentType, propsIdea, new CategoryAttribute(propsIdea.PropertyType.Name));
}
return new PropertyDescriptorCollection(propArray);
}

public override System.ComponentModel.PropertyDescriptorCollection GetProperties(object component)
{
return this.GetProperties(component, null);
}

// Provides the name for the event property tab.
public override string TabName
{
get
{
return "Events by Type";
}
}

// Provides an image for the event property tab.
public override System.Drawing.Bitmap Bitmap
{
get
{
Bitmap bmp = new Bitmap(DeserializeFromBase64Text(img));
return bmp;
}
}

// This method can be used to retrieve an Image from a block of
// Base64-encoded text.
private Image DeserializeFromBase64Text(string text)
{
Image img = null;
byte[] memBytes = Convert.FromBase64String(text);
IFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream(memBytes);
img = (Image)formatter.Deserialize(stream);
stream.Close();
return img;
}
} // end class
} // end namespace

DarenYong at 2007-9-8 > top of Msdn Tech,Visual Studio,Visual Studio Extensibility...
# 3
Hi,

Adding buttons to the property browser is only extensible from native code.

The Browse object that are returned from the ISelectionContainer in addition to implementing IDispatch implement “IVSMDPerPropertyBrowsing” (see vsmanaged.idl). This interface lets you hook up an implementation of a managed “PropertyTabAttribute” object and related objects (e.g. PropertyTab). You then have to have private communication between your PropertyTabAttribute/PropertyTab derived class and your managed browse object. Presumeably you need to invent a private interface for this. Your browse object would also implement this private interface. You would cast “component” that is passed to your “PropetyTab GetTabDispatch(object component)” method to your private interface. You should look on MSDN for the documentation of PropertyTabAttribute and PropertyTab. They are part of the .NET Framework.

Gaston at 2007-9-8 > top of Msdn Tech,Visual Studio,Visual Studio Extensibility...
# 4

Hello,

Well that sounds rather more complicated than I was hoping. Currently, my browse object looks like:
[DefaultProperty("Name")]
[PropertyTabAttribute(typeof(WorkflowObjectPropertyTab), PropertyTabScope.Document)]

public class WorkflowDocumentPropertyAdapter : PropertyAdapter
{
private WorkflowDocument document;

#region properties
[Description("Name of the model."), Category("Default")]
public string Name
{
get
{
return this.document.Name;
}
set
{
ModelChangedEventArgs e = new ModelChangedEventArgs(new ModelChange(ModelChangeType.ObjectValue, this.document.Id, WorkflowObjectType.Document, WorkflowObjectProperty.Name));
this.RaiseBeforeModelChanged(e);
if (e.Cancel)
{
return;
}
this.document.Name = value;
this.RaiseModelChanged(e);
}
}

[Description("Image to display on the design canvas."), Category("Appearance")]
public string BackgroundImage
{
get
{
return string.Empty;
}
}

#endregion properties
} // end class
Are you suggesting that I make WorkflowDocumentPropertyAdapter class implement the IVSMDPerPropertyBrowsing interface?

BTW, I cannot find any reference to this interface in the docs (or for IVSPerPropertyBrowsing). Are you sure that is the correct interface name?

****************
Can anyone from microsoft confirm the proper way to implement property tabs for the property browser in VSIP using the managed classes?

Best regards,
Daren.

DarenYong at 2007-9-8 > top of Msdn Tech,Visual Studio,Visual Studio Extensibility...

Visual Studio

Site Classified