|
发表于 2009-5-26 17:16:00
|
显示全部楼层
三、利用反射改变图元的属性
February 05, 2007
Using .NET reflection with AutoCAD to change object properties - Part 1
I ended up having a fun weekend, in spite of the circumstances. My wife and eldest son were both sick with a cold (which, inevitably enough, I now feel I’m coming down with myself), so we mostly stayed around at home. So I got to spend a little time working on an idea that came to me after my last post.
While the code I provided last time does pretty much exactly what was asked of it (allowing the person who requested it to change the colour of every entity in the modelspace – whether in nested blocks or not – for them to be coloured “by block”), it occurred to me that to be a really useful tool we should go one step further and enable two things:
a) Allow the user to specify the kind of object to apply the change to
b) Allow the user to select the specific property that should be changed
What we'd actually end up with, doing this, is a CHPROP on steroids - a command-line interface to provide deep object property modification (down through nested blocks), on any kind of object (as long as it - or one of its ancestor classes - provides a .NET interface). This is cool functionality for people needing to go and massage data, though clearly is potentially quite a scary tool in the wrong hands (thank goodness for the undo mechanism!).
The specific programming problem we need to solve comes down to runtime querying/execution of code and is quite interesting: one that’s easy to solve in LISP (thanks to the combination of the (read) and (eval) functions) and in COM (using type information to call through to IDispatch-declared functions exposed via the v-table), but is almost impossible in C++. With ObjectARX you can use class factories to create instances of objects that were not compiled in (we do this when we load a drawing – we find out the class name of each object being read in, call its static class factory method available in the class’ corresponding AcRxClass object, and pass the information we read in to the dwgInFields() function to resurrect a valid instance of the object). But it’s much harder to query at runtime for a particular method to be called – you could hardcode it for the built-in protocol, but any new objects would cause a problem.
But anyway – all this to say that the way to do it in .NET is to use our old friend Reflection (I love it when a couple of recent topics converge like this, although I wish I could say it was all part of some grand plan… :-)
So, there are four things we actually need to use reflection for in this sample:复制代码That's basically all there is to it, but I'm going to drag this out over a couple of posts, as the code does get quite involved.
This first post is going to focus on the user-input aspect of the code - querying the user for the various bits of information we need, and getting the type & property information using reflection. So it really only looks at the first two items on the above list.
Here's the C# code itself - the main user-input function is called SelectClassPropertyAndValue(), and I've defined a simple TEST command to simply call it and return the results.-
- using Autodesk.AutoCAD.ApplicationServices;
- using Autodesk.AutoCAD.DatabaseServices;
- using Autodesk.AutoCAD.EditorInput;
- using Autodesk.AutoCAD.Runtime;
- using Autodesk.AutoCAD.Colors;
- using System.Reflection;
- namespace PropertyChanger
- {
- public class PropertyChangerCmds
- {
- [CommandMethod("TEST")]
- public void TestInput()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
- System.Type objType;
- string propName;
- object newPropValue;
- bool recurse;
- if (SelectClassPropertyAndValue(
- out objType,
- out propName,
- out newPropValue,
- out recurse
- )
- )
- {
- ed.WriteMessage(
- "\nType selected: " + objType.Name +
- "\nProperty selected: " + propName +
- "\nValue selected: " + newPropValue +
- "\nRecurse chosen: " + recurse
- );
- }
- else
- {
- ed.WriteMessage(
- "\nFunction returned false."
- );
- }
- }
- private bool SelectClassPropertyAndValue(
- out System.Type objType,
- out string propName,
- out object newPropValue,
- out bool recurse)
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Editor ed = doc.Editor;
- objType = null;
- propName = "";
- newPropValue = null;
- recurse = true;
- // Let's first get the class to query for
- PromptResult ps =
- ed.GetString(
- "\nEnter type of objects to look for: "
- );
- if (ps.Status == PromptStatus.OK)
- {
- string typeName = ps.StringResult;
- // Use reflection to get the type from the string
- objType =
- System.Type.GetType(
- typeName,
- false, // Do not throw an exception
- true // Case-insensitive search
- );
- // If we didn't find it, try prefixing with
- // "Autodesk.AutoCAD.DatabaseServices."
- if (objType == null)
- {
- objType =
- System.Type.GetType(
- "Autodesk.AutoCAD.DatabaseServices." +
- typeName + ", acdbmgd",
- false, // Do not throw an exception
- true // Case-insensitive search
- );
- }
- if (objType == null)
- {
- ed.WriteMessage(
- "\nType " + typeName + " not found."
- );
- }
- else
- {
- // If we have a valid type then let's
- // first list its writable properties
- ListProperties(objType);
- // Prompt for a property
- ps = ed.GetString(
- "\nEnter property to modify: "
- );
- if (ps.Status == PromptStatus.OK)
- {
- propName = ps.StringResult;
- // Make sure the property exists...
- System.Reflection.PropertyInfo propInfo =
- objType.GetProperty(propName);
- if (propInfo == null)
- {
- ed.WriteMessage(
- "\nProperty " +
- propName +
- " for type " +
- typeName +
- " not found."
- );
- }
- else
- {
- if (!propInfo.CanWrite)
|
|