• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 775
  • Last Modified:

propertygrid hosting - providing services

I'm hosting a PropertyGrid control.  The objects being edited contain some properties that have custom editors assigned to them.

All editors implement this method:

object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)

What I'm trying to figure out is where the "IServiceProvider provider" comes from, or how to extend it.  I want to be able to implement additional services in the host application, and allow type editors to retrieve those services using provider.GetService(typeof(MyService)).

I've tried deriving from PropertyGrid and overriding its GetService method, but couldn't get that to work.  I've also tried implementing an ISite interface and assigning it to the Site property of the PropertyGrid, and couldn't get that to work either.  I don't know whether I was just missing something simple, or was completely on the wrong track.  Either way, these points are for a working example of how to expose a custom service to a type editor.
0
JMoon5FTM
Asked:
JMoon5FTM
  • 4
  • 3
1 Solution
 
_Katka_Commented:
Hi, registering your own service is particulary easy. You'll need a component (=> control) from which you'll register your service (which implements IMyServiceProvider in the example bellow) to a designer host. Check the code sample and override the Site property. Your service will get registered to a design host, and can be then retrieved by any IServiceProvider on the host:

IServiceProvider.GetService(typeof(IMyServiceProvider)) as MyServiceProvider;

regards,
Kate

Note: It's written by the hand; there're possible typos ahead.
public override ISite Site
{
  set
  {
    base.Site = value;
 
    if (value != null)
    {
      IDesignerHost designerHost = value.Container as IDesignerHost; 
 
      if (designerHost != null) // works only in design-time
      {
        // checks whether my service is already registered or not
        IMyServiceProvider myServiceProvider = designerHost.GetService(typeof(IMyServiceProvider)) as IMyServiceProvider;
 
        // if my service isn't registered yet; do it
        if (myServiceProvider == null)
        {
          // creates the instance of my service
          myServiceProvider = CreateMyServiceProvider(designerHost);
          designerHost.AddService(typeof(IMyServiceProvider), myServiceProvider);
        }
      }
    }
  }
}

Open in new window

0
 
JMoon5FTMAuthor Commented:
Hello, and thank you for your response.  However I may not have been clear enough.

I'm hosting the PropertyGrid control in my own application.  So, this is happening at run-time as far as the PropertyGrid is concerned.  I tried overriding the Site property, but can confirm that it's not currently invoked at all.

Do I need to implement IDesignerHost myself?
0
 
_Katka_Commented:
Oh, so you're trying to implement your own designer application ? Because Site property is accessible only in Visual Studio or applications with their own IDesignerHost. Please describe in more detail a current type of the application. Is it some kind of editor ? Because you don't need IServiceProvider to share data/methods among the property editors. What I need to know is the goal, this way I'll be able to focus my help more precisely. :)

regards,
Kate
0
Granular recovery for Microsoft Exchange

With Veeam Explorer for Microsoft Exchange you can choose the Exchange Servers and restore points you’re interested in, and Veeam Explorer will present the contents of those mailbox stores for browsing, searching and exporting.

 
JMoon5FTMAuthor Commented:
Yes, this is my own application using the PropertyGrid to let the user configure some objects, some of which are internal and some of which come from external DLLs.  Some of the external DLLs may be used from Visual Studio as well.  I need to extend the user interface a bit to provide some extra features, such as detailed descriptions of some of the dropdown options.  The UI stuff is no issue - I can get the PropertyGrid to do what I want.  The problem is exposing the extra UI functions to type editors that might want to use the functions.  I thought the best way to do that would be using the service provider that's passed to the type editor, so the type editor could be designed like this:

public object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
     SpecialUiService sus = provider.GetService(typeof(SpecialUiService)) as SpecialUiService;
     if(sus != null)
     {
          // use the extra features

I'm trying to figure out what I need to do from the app side to make the provider.GetService call succeed.
0
 
_Katka_Commented:
I'd recommend you to download this sample application but basically you'll implement your own IDesignerHost. The sample is bloated with functionality which is not all needed so I'll point out the basic steps to take not of from the sample.

1) implement containing control (usually a main form or a control element in a case of the MVC pattern)
2) implement MyDesignerHost : IDesignerHost (=> IServiceContainer, IServiceProvider)
 2a) register services in constructor
 2b) implement IServiceProvider.GetService() method (basically you'll encapsulate IServiceContainer)
3) store the MyDesignerHost in your main form
4) access your MyDesignerHost in the form to retrieve the service => myDesignerHost.GetService(..)

http://support.microsoft.com/kb/813808

regards,
Kate
0
 
JMoon5FTMAuthor Commented:
I've got it working!  Thank you Katka for pointing me in the right direction.

In case anyone else tries to do this, I'll lay out the solution simply:

* You need an implementation of the ISite interface, but only the IServiceProvider interface (from which ISite derives) is actually needed.  The rest of the members of ISite can be stubs.  This object has to be assigned to the PropertyGrid's Site property.

* You need an implementation of the IDesignerHost interface, but again only the IServiceProvider interface (from which IDesignerHost derives) is actually needed.  The rest of the members of IDesignerHost (of which there are many) can be stubs.  This object has to be returned from the site object's implementation of GetService(typeof(IDesignerHost)).  This is the object that will be queried when a type editor asks for a service.

This would have been much easier to figure out if the PropertyGrid allowed you to provide an IServiceProvider directly, or if it called its virtual GetService() method, or if it would just use the IServiceProvider implementation from ISite directly instead of using it just to get at yet another IServiceProvider in the form of IDesignerHost.

Well thanks again Katka!
        // IDesignerHost and ISite implemented together,
        // for convenience
 
        private class DesignerHost : IDesignerHost, ISite
        {
// unnecessary ISite members
            public IComponent Component { get { return null; } }
            public IContainer Container { get { return null; } }
            public bool DesignMode { get { return false; } }
            public string Name { get { return null; } set { } }
// unnecessary IDesignerHost members
            // public IContainer Container -- already implemented as part of ISite
            public bool InTransaction { get { return false; } }
            public bool Loading { get { return false; } }
            public IComponent RootComponent { get { return null; } }
            public string RootComponentClassName { get { return String.Empty; } }
            public string TransactionDescription { get { return String.Empty; } }
            public void Activate() { }
            public IComponent CreateComponent(Type componentClass, string name) { return null; }
            public IComponent CreateComponent(Type componentClass)
            {
                return CreateComponent(componentClass, null);
            }
            public DesignerTransaction CreateTransaction(string description) { return null; }
            public DesignerTransaction CreateTransaction()
            {
                return CreateTransaction(null);
            }
            public void DestroyComponent(IComponent component) { }
            public IDesigner GetDesigner(IComponent component) { return null; }
            public Type GetType(string typeName) { return null; }
            public void AddService(Type serviceType, object serviceInstance, bool promote) { }
            public void AddService(Type serviceType, ServiceCreatorCallback callback, bool promote) { }
            public void AddService(Type serviceType, object serviceInstance)
            {
                AddService(serviceType, serviceInstance, false);
            }
            public void AddService(Type serviceType, ServiceCreatorCallback callback)
            {
                AddService(serviceType, callback, false);
            }
            public void RemoveService(Type serviceType, bool promote) { }
            public void RemoveService(Type serviceType)
            {
                RemoveService(serviceType, false);
            }
            public event EventHandler Activated;
            public event EventHandler Deactivated;
            public event EventHandler LoadComplete;
            public event DesignerTransactionCloseEventHandler TransactionClosed;
            public event DesignerTransactionCloseEventHandler TransactionClosing;
            public event EventHandler TransactionOpened;
            public event EventHandler TransactionOpening;
 
// IServiceProvider - the "meat"
            public object GetService(Type serviceType)
            {
                if (serviceType == typeof(IDesignerHost)) return this;
                else if (serviceType == typeof(MyCustomService)) return new MyCustomService();
                else return null;
            }
        }
 
 
 
PropertyGrid1.Site = new DesignerHost();

Open in new window

0
 
_Katka_Commented:
You're welcome :)

To refine the code you can wrap the IServiceContainer as a field in the MyDesignerHost (as pointed out in the sample):

private IServiceContainer serviceContainer;

Then initialize it in the constructor:

public MyDesignerHost(IServiceProvider serviceProvider)
{
    this.serviceContainer = new ServiceContainer(serviceProvider);
    ...
}

And then use it to add service:

serviceContainer.AddService(...);

or GetService (in the IDesignerHost method):

serviceContainer.GetService(...)

So your IDesignerHost implementation can be more generic:

Object IServiceProvider.GetService(Type serviceType)
{
    return serviceContainer.GetService(serviceType);
}

but that's just for the final touch.

regards,
Kate
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now