Creating / Using controls via Reflection

Ribbon for WPF Forum

Posted 5 years ago by Manuel Eisenschink - Twisted Arts
Version: 14.1.0602
Environment: Windows 8 (64-bit)
Avatar

Hello,

I got the following problem. For my application I need to create the user-interface at runtime (during start-up).
For this I created a Pipeline-system which reads my UI-description file (a xml file with declarations of UI elements hierarchically ordered) which worked absolutely fine, until yet.

Previously I used default WPF controls and the Fluent Ribbon controls. At this time everything worked as it should.

So after a while I decided to use professional solutions and chose ActiPro. I changed all controls (which was pretty easy) and applied the changes to my xml file. But now it still doesn't work.

My pipeline adds dynamically bindings to specified properties to enable translations. I'm creating the controls basically via the Activator-class (Activator.CreateInstance()) and then search for the DependencyProperty via Reflection. On every ActiPro control it has the same strange behaviour:

It can't find any fields though the dependency properties are visible in the debugger. And when you go up the hierarchy in the debugger (now I'm talking about the Button control in the Ribbon library) there it is about 6-7 times "Button" and then 1-2 "Dispatcher". It's not the hierarchy you can find in the Object Browser.

EDIT: Okay, I found out that it is obfuscated which makes it impossible to use Reflection. So, is there the possiblity to get non-obfuscated libraries without buying the Blueprint license? That'd be great. Otherwise I would have to rewrite the biggest part of my application...

Thanks

Manuel

[Modified 5 years ago]

Comments (4)

Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Manuel,

Could you go into more detail about how your process works and perhaps show some sample snippets of your XML.  I wouldn't think that there would be any problem using Activator.CreateInstance() on Actipro Ribbon controls since that would just create them with the public constructors.  But if you are also trying to create controls that are part of our controls' templates or other internal-only properties, perhaps that is where you are running into issues.


Actipro Software Support

Posted 5 years ago by Manuel Eisenschink - Twisted Arts
Avatar
        <Object id="ctg_test" type="acti:con.ContextualTabGroup" order="0">
          <Meta>
            <MetaTag id="SubControlEnabled">True</MetaTag>
            <MetaTag id="IsMultiSubControl">True</MetaTag>
            <MetaTag id="SubControlProperty">Items</MetaTag>
          </Meta>
          <Set>
            <Translate section="IDEWindow?Ribbon?ContextualGroups" key="ObjectEditing" />
          </Set>
          <Set property="Color" type="prco:System.Windows.Media.Color">
            <Object type="prco:System.Windows.Media.Color">
              <Set property="A">255</Set>
              <Set property="R">200</Set>
              <Set property="G">20</Set>
              <Set property="B">80</Set>
            </Object>
          </Set>
          <Set property="IsActive">True</Set>
          <Object id="tab_grpTest" type="acti:con.Tab" order="0">
            <Set property="Label" type="string">Test Tab</Set>
          </Object>
        </Object>

That's who a control is defined. The system is highly flexible. You can create every control even those who require constructor parameters. It's not just for controls, it also creates other objects which Add-Ins have to create/register in the application.

The control gets created by reading out the type and instanciating it via the Activator. Afterwards the Set-Nodes are getting parsed and the pipeline uses Reflection to set the values of the properties. By explicitly specifing BindingFlags you can even set private properties. In this case I just used publicly available commonly used ones.

In this case a Tab gets created and appended to ContextualTabGroup's child-items.

 

The problem is that there's no way of finding any DependencyProperty. It always returns an empty array or only one which is not the one I need. I guess that Reflection does not work on those, right? I already tried to use a different approach, using XAML as definition for those controls. Sadly this lacks a lot of those features I implemented in the last few months. It interrupts the menu-system and required me to rewrite a lot.

 

Is there any solution? That's a bad situation. I can't throw the system over and also I can't spend the next weeks developing a completely new system if there was a solution. Now I'm stuck and can't do much as this issue disrupts the whole application.

Thanks in advance,
Manuel

Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Manual,

Ok so it seems like the obfuscation of our assemblies might be causing some problems with your custom deserialization mechanism.  I would think that as long as you stick with public members, you would be ok though.  The public properties that use dependency property backers should have those DependencyProperty fields public too.  When I use Reflector and look for that scenario, I do see the DependencyProperty fields as public.  If they weren't, then I doubt our product would work properly.

Of course there will be internal/private members too, some of which might be DependencyProperty fields.  You shouldn't be touching though.  Anything that you should be serializing/deserializing should be public.

Reflection should work with obfuscated assemblies.  It's just that some names of the internal/private members will be mangled.


Actipro Software Support

Answer - Posted 5 years ago by Manuel Eisenschink - Twisted Arts
Avatar

Okay I now use a hybrid-approach: Everything is being created dynamically but Actipro controls. These, when read out, are being created hard-coded. But the translation still does not work. I want to be able to at least set the public properties and apply bindings to them dynamically. This still doesn't work, because no DependencyProperty can be found.

I tried everything but fail at getting it via Reflection. And as the Object Browser says, these are no internal/private members. So they have to be accessible, right? Am I missing something or is this a strange internal bug?

        private DependencyProperty GetDependencyProperty(object obj, string name, BindingFlags customFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.GetField)
        {
            return (from field in obj.GetType().GetFields(customFlags)
                    where field.FieldType == typeof(DependencyProperty)
                    select field).Where(fi => fi.Name == name)
                                 .Select(fi => (DependencyProperty)fi.GetValue(null))
                                 .FirstOrDefault();
            
        }

This snippet worked perfectly with all classes. Even non-controls. Never a single error. Any ideas? Applying bindings dynamically is a really important thing. Hard-coding would be a very expensive task.

By the way: Using GetField doesn't work either, if that was a suggestion of anybody :D

EDIT: Problem solved. I just found a tiny comment at Stackoverflow. As your controls inherit the LabelProperty from the primitive classes, you have to use the BindingFlags.FlattenHierarchy. Now it works just fine :)

Thanks

Manuel

[Modified 5 years ago]

The latest build of this product (v2018.1 build 0675) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.