Sign in to follow this  
  • entries
    23
  • comments
    28
  • views
    20191

.NET Designer Fun

Sign in to follow this  

159 views

Had a good bit of fun today implementing the Visual Studio Designer end of CodeEngine. Prior to this, the Engine property of my rnfnCodeLite control had been non-interactive in the Designer. Now you can select all known classes that inherit CodeEngine, and aren't abstract. You can also set the properties of the class from the PropertyGrid. Implementing this was annoying as heck at times, but I managed to get it all done in one day, and I'd say that's a fair bit of work, especially considering the lack of it of late. :p

Firstly, the bit about selecting known classes that inherit CodeEngine:
    internal class CodeEngineUITypeEditor:UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
public override object EditValue(ITypeDescriptorContext context,IServiceProvider provider,object value)
{
if(context!=null&&provider!=null)
{
IWindowsFormsEditorService editorService=(IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));

if(editorService!=null)
{
CodeEngineListBox listBox=new CodeEngineListBox(editorService,context);
editorService.DropDownControl(listBox);
return listBox.SelectedItem;
}
}
return base.EditValue(context,provider,value);
}
}
internal class CodeEngineListBox:ListBox
{
IWindowsFormsEditorService editorService=null;

public CodeEngineListBox(IWindowsFormsEditorService service,ITypeDescriptorContext context)
{
editorService=service;
Sorted=true;
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
foreach(Type type in assembly.GetTypes())
if(type.IsSubclassOf(typeof(CodeEngine)))
Items.Add(Activator.CreateInstance(type,(context.Instance as rnfnCodeLite)));
SelectedIndexChanged+=OnSelectedIndexChanged;
}

void OnSelectedIndexChanged(object sender,EventArgs e)
{
editorService.CloseDropDown();
}

}



Two bits here. The CodeEngineUITypeEditor bit just tells the property grid that we're a drop-down UITypeEditor(meaning we're to be used "within" the property grid, versus being a dialog window) and what to do when we're being used. The only thing we do is add a CodeEngineListBox, which is our second bit. All this does is extend System.Windows.Forms.ListBox and displays every implementable type that is a subclass of CodeEngine. The only downside, at the moment, is the first time you click it in the Designer, it takes quite a few seconds to go through all the assemblies looking for appropriate types. It gets faster the more you use it(or at least in my quick two-minute testing), but the first time is still a doozy. JPatrick in #gamedev@AfterNET.org was discussing it with me, and I think he said there was a way to get all the types from TypeDescriptor.GetEditor, but I didn't really understand all of it. :x

Right, so apparently what I wanted already existed. Imagine that. :p System.ComponentModel.ExpandableObjectConverter for those of you interested.
Secondly, I wanted to be able to implement any and all properties of any and all classes that inherited CodeEngine. I'm treating any properties visible to the property grid in these classes as being options/settings for the engine. I've already implemented a runtime feature where right-clicking on the 'deadspace' area between the scrollbars pops up a dialog window showing a property grid with the properties for the engine, so everything is all set runtime. I wasn't quite sure how I wanted to do this in the Designer until I took a look at the property grid, and noticed that some properties had sub-properties, such as Font, Size, and Rectangle types. After a bit of research, I found out this was due to having a TypeConvertor, which is implemented here:
internal class CodeEngineConverter:TypeConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context,object value,Attribute[] attributes)
{
return TypeDescriptor.GetProperties(value).Sort();
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return context.Instance.GetType().IsSubclassOf(typeof(rnfnCodeLite));
}
public override object CreateInstance(ITypeDescriptorContext context,System.Collections.IDictionary propertyValues)
{
if(propertyValues==null)
throw new ArgumentException("propertyValues");

CodeEngine engine=Activator.CreateInstance((context.Instance as rnfnCodeLite).Engine.GetType(),(context.Instance as rnfnCodeLite)) as CodeEngine;
foreach(PropertyInfo info in engine.GetType().GetProperties())
{
if(propertyValues[info.Name]!=null)
info.SetValue(engine,propertyValues[info.Name],null);
}

return engine;
}
public override bool CanConvertFrom(ITypeDescriptorContext context,Type sourceType)
{
return((sourceType==typeof(string)||base.CanConvertFrom(context,sourceType))&&context.Instance.GetType().IsSubclassOf(typeof(rnfnCodeLite)));
}
public override object ConvertFrom(ITypeDescriptorContext context,System.Globalization.CultureInfo culture,object value)
{
if(!String.IsNullOrEmpty((string)value)&&context.Instance.GetType().IsSubclassOf(typeof(rnfnCodeLite)))
{
string[] str=(value as string).Trim().Split(culture.TextInfo.ListSeparator[0]);

CodeEngine engine=Activator.CreateInstance((context.Instance as rnfnCodeLite).Engine.GetType(),(context.Instance as rnfnCodeLite)) as CodeEngine;
PropertyInfo[] propertyInfo=engine.GetType().GetProperties();

if(propertyInfo.Length==str.Length)
{
for(int i=0;i propertyInfo.SetValue(engine,TypeDescriptor.GetConverter(propertyInfo).ConvertFromString(context,culture,str),null);

return engine;
}
}
return base.ConvertFrom(context, culture, value);
}
}


Bleh. Seems like WordPress's Visual editor doesn't like the ""bit, even in tags. So I lost all my typing after it since it tried to turn it into a flash object. Anywho, implementing TypeConverter so that the property grid likes it took a bit of hacking and cookbooking. The msdn documentation on how to do it didn't seem to be sufficient, so I had to cobble something together from that and Reflection. So I'm not sure how it'll hold under heavy testing, but it seems to work so far. The only thing that I can see off the top of my head is if any property is of a type that doesn't have a TypeConverter, it might break.

Oh, yeah, a bit I lost due to WordPress: I'm using properties that are visible by the property grid as options/settings for any CodeEngine class. I thought it'd be neat to be able to do it in the property grid design time. Run time wise, I've already implemented it. My little "deadspace feature" is now overloaded. When left-clicked, it'll pop-out an external editor or toggle whether the current external editor is on-top or not. Now when it is right-clicked, it'll show a dialog window with a property grid displaying the properties of the current engine.

In closing, I'm quite proud of the get/set-all-the-properties in my custom TypeConverter. With a bit of work and polish, I think there is a good ObjectTypeConvertor lurking in there for the property grid.
Mirrored here
Sign in to follow this  


1 Comment


Recommended Comments

Ehrm, found a problem with the TypeConvertor implementation. Seems other Controls are trying to use it as well, or something weird. Completely borking up my using the property grid during runtime.

[Edit:] Nevermind, fixed the problem and updated the code. I was presuming context.Instance was always going to be an rnfnCodeLite control, and not the CodeEngine instance itself.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Important Information

By using GameDev.net, you agree to our Terms of Use and Guidelines.

Create an account with GameDev.net and join the game development conversation on our forums, blogs, articles, and more!

Sign me up!