Udostępnij za pośrednictwem


Use the power of Reflection to create and manipulate managed objects

When you create a Windows Presentation Foundation (WPF) project in Visual Studio, references to WPF libraries, like PresentationFrameworks.dll, PresentationCore.dll, WindowsBase.dll, are automatically added, from which you can create types and invoke members.

That makes the code to create a couple controls and show them in a window quite simple. (A WPF application already creates a window for you). This sample directly manipulates properties, events, methods, and enums.

 

     private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
      var sp = new StackPanel();
      var btn = new Button()
        {
          Width = 100,
        };
      btn.Click += Btn_Click;
      btn.Content = "_hit me";
      var txtbox = new TextBox()
      {
        AcceptsReturn = true,
        IsReadOnly = true,
        Height = 540,
        VerticalScrollBarVisibility = ScrollBarVisibility.Auto //1
      };
      for (int i = 0; i < 40; i++)
      {
        txtbox.AppendText(string.Format("Some Text{0}\r\n", i));
      }
      sp.Children.Add(btn);
      sp.Children.Add(txtbox);
      this.Content = sp;
    }

    private void Btn_Click(object sender, RoutedEventArgs e)
    {
      this.Title = "Clicked!";
    }

I wanted to write the same functionality in a C# console application, with no WPF references (Menu->Project->Add Reference->) . It uses System.Reflection to load the WPF libraries at runtime and instantiates types and invoke members. At edit/compile time, there is no knowledge of WPF types (and thus no WPF intellisense).

This technique can be used to load and write code to manipulate any managed types, not just those of WPF.

For example, below is a C# console application that has no references to any WPF libraries. It does call System.Reflection.Assembly.LoadFrom, with the path to PresentationFramework.dll to load some WPF types.

The code gets the System.Type of various items, such as a WPF Window, Button, TextBox, StackPanel. From the type, it calls the InvokeMember with the BindingFlags.CreateInstance to create an actual Window or Button.

BindingFlags.SetProperty, BindingFlags.GetProperty and BindingFlags.InvokeMethod are used to manipulate the objects. Using enums (like ScrollBarVisibility) is a little different.

Hooking up events is a little bit trickier.

See also:

Sample using CPP/Clr: Call managed code from your C++ code

To use the sample code:

Start Visual Studio (any recent version should work)

File->New->Project->C#->Console Application

<code>

 using System;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
  class Program
  {
    static object _oWindow;
    static Type _typWindow;
    [STAThread]
    static void Main(string[] args)
    {
      var program = new Program();
      program.doit();
    }
    void doit()
    {
      var asmPFWkName = @"C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll";
      var asmPresentationFramework = Assembly.LoadFrom(asmPFWkName);
     _typWindow = asmPresentationFramework.GetType("System.Windows.Window");
      _oWindow = _typWindow.InvokeMember(
          name: null,
          invokeAttr: BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: null,
          args: null);
      var typStackPanel = asmPresentationFramework.GetType("System.Windows.Controls.StackPanel");
      var oStackPanel = typStackPanel.InvokeMember(
          name: null,
          invokeAttr: BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: null,
          args: null);
      var typButton = asmPresentationFramework.GetType("System.Windows.Controls.Button");

      var btnClickEventInfo = typButton.GetEvent("Click");
      var asmPresentationCore = AppDomain.CurrentDomain.GetAssemblies().Where(asm => asm.FullName.Contains("PresentationCore")).Single();
      var typRoutedEventHandler = asmPresentationCore.GetType("System.Windows.RoutedEventHandler");
      var oButton = typButton.InvokeMember(
          name: null,
          invokeAttr: BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: null,
          args: null);

      var meth = typeof(Program).GetMethod("btnClick");
      Delegate delBtnClick = Delegate.CreateDelegate(typRoutedEventHandler, null, meth);
      btnClickEventInfo.AddEventHandler(oButton, delBtnClick);
      typButton.InvokeMember(
          name: "Height",
          invokeAttr: BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: oButton,
          args: new object[] { 20 }
          );
      typButton.InvokeMember(
          name: "Width",
          invokeAttr: BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: oButton,
          args: new object[] { 100 }
          );
      typButton.InvokeMember(
          name: "Content",
          invokeAttr: BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: oButton,
          args: new[] { "_hit me" }
          );
      var typTxtBox = asmPresentationFramework.GetType("System.Windows.Controls.TextBox");
      var oTxtBox = typTxtBox.InvokeMember(
          name: null,
          invokeAttr: BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: null,
          args: null);
      typTxtBox.InvokeMember(
          name: "Height",
          invokeAttr: BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: oTxtBox,
          args: new object[] { 400 }
          );
      var typScrollBarVisibility = asmPresentationFramework.GetType("System.Windows.Controls.ScrollBarVisibility");
      var enumscrollBarVisibility = typScrollBarVisibility.GetEnumValues();
      typTxtBox.InvokeMember(
          name: "VerticalScrollBarVisibility",
          invokeAttr: BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: oTxtBox,
          args: new object[] { enumscrollBarVisibility.GetValue(1) }
          );
      for (int i = 0; i < 50; i++)
      {
        typTxtBox.InvokeMember(
            name: "AppendText",
            invokeAttr: BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,
            binder: null,
            target: oTxtBox,
            args: new[] { string.Format("some text{0}\r\n",i) }
            );
      }
      var ospChildren = typStackPanel.InvokeMember(
          name: "Children",
          invokeAttr: BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: oStackPanel,
          args: null
          );
      // "StackPanel.Children is a "System.Windows.Controls.UIElementCollection"
      var typUICollection = asmPresentationFramework.GetType("System.Windows.Controls.UIElementCollection");
      typUICollection.InvokeMember(
          name: "Add",
          invokeAttr: BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: ospChildren,
          args: new[] { oButton }
          );
      typUICollection.InvokeMember(
          name: "Add",
          invokeAttr: BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: ospChildren,
          args: new[] { oTxtBox }
          );

      _typWindow.InvokeMember(
          name: "Title",
          invokeAttr: BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: _oWindow,
          args: new[] { "Window Title" }
          );
      _typWindow.InvokeMember(
          name: "Content",
          invokeAttr: BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
          binder: null,
          target: _oWindow,
          args: new[] { oStackPanel }
          );
      _typWindow.InvokeMember(
          name: "ShowDialog",
          invokeAttr: BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod,
          binder: null,
          target: _oWindow,
          args: null
          );
    }
    public void btnClick(object sender, object args)
    {
      _typWindow.InvokeMember(
          name: "Title",
          invokeAttr: BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.Public,
          binder: null,
          target: _oWindow,
          args: new[] { "Button Clicked!" }
          );
    }
  }
}

</code>