Hosting WPF content inside an AutoCAD palette

OK, OK: I know I said I'd talk more about overrules, last week, but – as is often the way, I'm afraid to say – I got distracted. The good news, though, is that I got distracted by something genuinely interesting, and well worth sharing.

I've been working on upgrading the WinForms user interface of an existing .NET application to use WPF, the Windows Presentation Foundation. For those wanting a thorough grounding in WPF, I recommend watching Fenton Webb's highly-rated webcast series on WPF, just one of the interesting webcasts that can be downloaded and viewed from our API training schedule:

AutoCAD: Using WPF in your Applications - Part 1 (28.9 Mb)

AutoCAD: Using WPF in your Applications - Part 2 (16.3 Mb)

In this post I'm going to walk through creating a simple WPF User Control which we can then host inside an AutoCAD PaletteSet.

Firstly we need to add a WPF User Control to our Visual Studio 2008 (or higher) project:

Adding a WPF User Control to our project

We'll accept the default name for the purposes of this project.

Now we should see our blank user-control hosted in both a designer window and a XAML editing pane:

Our empty WPF User Control

For the content of our user-control, I decided to take a static 2D image and apply some effects to it, inspired by this page, which also has a thorough explanation of the various effects applied (which saves me from doing the same :-).

You can copy & paste this XAML into the editing pane:

<UserControl x:Class="WPF_Palette.UserControl1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Height="380" Width="300">

  <StackPanel Background="Gray">

    <Border BorderBrush="OldLace" BorderThickness="10" CornerRadius="10"

            HorizontalAlignment="Center" VerticalAlignment="Center">

      <Image Source="TTIF.png" Width="200" Height="200"

             Stretch="Fill" x:Name="blogImage" />

      <Border.RenderTransform>

        <SkewTransform CenterX="250" CenterY="0"

                       AngleX="0" AngleY="350" />

      </Border.RenderTransform>

    </Border>

    <Border Width="220" Height="300" BorderThickness="10"

            CornerRadius="10" BorderBrush="OldLace">

      <Border.Background>

        <VisualBrush Visual="{Binding ElementName=blogImage}">

          <VisualBrush.Transform>

            <ScaleTransform ScaleX="1" ScaleY="-1"

                            CenterX="200" CenterY="150" />

       
60;  </
VisualBrush.Transform>

        </VisualBrush>

      </Border.Background>

      <Border.OpacityMask>

        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

          <GradientStop Offset="0" Color="Black" />

          <GradientStop Offset="0.3" Color="Transparent" />

        </LinearGradientBrush>

      </Border.OpacityMask>

      <Border.RenderTransform>

        <SkewTransform CenterX="260" CenterY="0"

                       AngleX="40" AngleY="350" />

      </Border.RenderTransform>

    </Border>

  </StackPanel>

</UserControl>

For this to work, I added a simple screenshot of this blog as an item in the project, calling it "TTIF.png". I simply added it to the project via the Solution Explorer, using Add –> Existing Item… no need to create a resource.

Now we should see our User Control has taken shape:

Our edited WPF User Control

Now it's simply a matter of defining a command to create a PaletteSet to host our WPF content:

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Windows;

using System.Drawing;

using System.Windows.Forms;

using System.Windows.Forms.Integration;

 

namespace WPF_Palette

{

  public class Commands

  {

    static PaletteSet _ps = null;

 

    [CommandMethod("WPFP")]

    public void ShowWPFPalette()

    {

      if (_ps == null)

      {

        // Create the palette set

 

        _ps = new PaletteSet("WPF Palette");

        _ps.Size = new Size(400, 600);

        _ps.DockEnabled =

          (DockSides)((int)DockSides.Left + (int)DockSides.Right);

 

        // Create our first user control instance and

        // host it on a palette using AddVisual()

 

        UserControl1 uc = new UserControl1();

        _ps.AddVisual("AddVisual", uc);

 

        // Create our second user control instance and

        // host it in an ElementHost, which allows

        // interop between WinForms and WPF

 

        UserControl1 uc2 = new UserControl1();

        ElementHost host = new ElementHost();

        host.AutoSize = true;

        host.Dock = DockStyle.Fill;

        host.Child = uc2;

        _ps.Add("Add ElementHost", host);

      }

 

      // Display our palette set

 

      _ps.KeepFocus = true;

      _ps.Visible = true;

    }

  }

}

For the application to build you'll need to add project references to the .NET assemblies System.Drawing and WindowsFormsIntegration (in addition to the usual AcMgd.dll and AcDbMgd.dll, of course).

The above code deliberately adds two instances of our control into the PaletteSet, to show the different techniques for doing so:

  1. The first uses the new (to AutoCAD 2010) AddVisual() method to add in our WPF User Control into a Palette
    • This works, but doesn't dock the control inside the palette (something I hope to see addressed in the future – as it stands this isn't particularly useful)
  2. The second uses an ElementHost to host our WPF User Control – essentially hosting our WPF control in one that works with a WinForms UI. We then use the standard Add() method to add it, as we have done in the past

Now the application should build. When we NETLOAD it into AutoCAD and run the WPFP command, we should see our docked palette set with the first tab visible:

Our first WPF Palette inside AutoCAD

We can see that this tab hosts our control, but it doesn't fill the palette.

If we select the second tab, we see things are better, there:

Our second WPF Palette inside AutoCAD

If you undock the palette set you can see more easily the way the control is hosted by each palette.

I'm still on the steep part of the learning curve with respect to WPF (and expect I'll be there for some time), but so far I'm really impressed with its capabilities, especially around binding a user-interface's properties to the data it hosts. I'll be sharing more of my findings related to WPF in due course.

36 responses to “Hosting WPF content inside an AutoCAD palette”

  1. WPF is indeed very nice with Autocad, more articles on this subject are more than welcome :).

  2. Excellent and interesting info Kean.

  3. web hosting pakistan Avatar
    web hosting pakistan

    nice post for the learning. good job

  4. hi Kean,
    Can I use WPF inside Autodesk Inventor 2009/2010?
    If yes can you guide me please?

    Thanks in advance,
    Pushpak

  5. Hi Pushpak,

    I have no idea - I imagine, you can, but doubt there are any UI integration points, from what little I know of Inventor's API.

    I suggest asking ADN (as I believe you're a member).

    Regards,

    Kean

  6. Kean,
    How do you get transparency working with a palette set and WPF using the ElementHost method?

    Thanks,
    Jason

  7. Jason,

    Sorry, I don't know - I haven't tried this myself. And I'm just starting a 4-day vacation (or trying to 🙂 so I suggest asking the ADN team or the AutoCAD .NET Discussion Group.

    Regards,

    Kean

  8. Excellent...Thanks K-Dubbs...!

  9. My pleasure, cd! 🙂

    On a related note, Albert showed an alternative technique for using AddVisual() during his AU session on .NET 4.0: he implemented an OnSize() (or equivalent) event handler that resized the user control whenever the WPF palette was resized. In case you'd rather not go through all the thunking with the ElementHost.

    Cheers,

    Kean

  10. Kean Walmsley Avatar

    Hi Vikas,

    This isn't a forum for support.

    Your comment doesn't appear to relate to this post, so please submit your future questions to the ADN team, if you're a member, or otherwise the AutoCAD .NET Discussion Group.

    That said, it sounds as though you're invoking the dialog directly from the code behind the QuickAccessToolbar button. I suggest firing off a command that displays your dialog, instead.

    Kean

  11. Hi,
    I am new to c# and I tried to run it but only the palatte shown with nothing in it.
    Can u help me with that.Thanks

  12. Sorry - there isn't enough information for me to help you.

    I suggest posting your project to the AutoCAD .NET Discussion Group, for someone to take a look at.

    Regards,

    Kean

  13. Hi Kean,

    In your code you has created a ElementHost instance, and linked it with uc2 variable. If you open other tab, and see ElementHost again - it is incorrect behaviour. ElementHost shall not be displayed on other tabs.

    Why you calmed down on it? Task is not solved.

    Regards, Andery.

  14. Hi Andrey,

    I'm not clear on what exactly you mean: are you trying to reuse the ElementHost on multiple tabs, or is there a problem with the above code?

    Either way, this isn't something I'm likely to revisit, at this stage: if this particular approach doesn't work for you then I suggest going with AddVisual() but implementing an OnSize() event-handler to fill the control as the containing palette gets resized, as mentioned here:

    keanw.com/2009/08/hosting-wpf-content-inside-an-autocad-palette.html#comment-6a00d83452464869e20128766fe3a2970c

    Regards,

    Kean

  15. Matthias Mueller Avatar

    Hi Kean,
    I know it's been a while since this topic was in focus. I can't solve this: I want my palette to appear on the bottom side (on top of the console). It's attached to the left side on startup. Is there a way to set a start position?

  16. Hi Matthias,

    Have you tried checking the Registry to see what info is stored when the palette is docked at the bottom vs. the left?

    Failing that I'd suggest posting to the discussion group: someone there must have come across this requirement.

    Regards,

    Kean

  17. Matthias Mueller Avatar
    Matthias Mueller

    Thanks for the advice, I posted my question to the AutoDesk .Net Group forums.autodesk.com/t5/NET/Custom-Palette-docked-at-the-bottom-side/td-p/4843273
    No answer yet :-/
    Which Registry do I need to check? How can I do this?

  18. Kean Walmsley Avatar

    There is probably some data stored on this in the Windows Registry. Use Regedit to dump AutoCAD's Registry info, create and dock a palette, quite AutoCAD and dump the section again. Diff the two files and see what's changed.

    Or use RegMon.

    Regards,

    Kean

  19. Matthias Mueller Avatar
    Matthias Mueller

    Hmm, no changes were recorded during Docking and Undocking Palettes in the Registry. The posted question in the .Net discussion group is still unanswered. Maybe my needs are a bit too specific 🙂
    I'll leave the palette docked at the left (default) side and provide the ability to dock it where the user wants it to be. Thanks Kean!

  20. Hi Kean! Very nice post. Can you show the code to add image to button in WPF toolbar. I added it. It shown on WPF designer but not show in autocad when it loaded!
    Thank you!

    1. Hi Vinh,

      For problems such as this I suggest posting code to the AutoCAD .NET Discussion Group: someone there will be able to help.

      Regards,

      Kean

  21. Excelent info.
    Kean, I can't set palette max. width size to 30px when it is fixed to the left side. Could you give me some advice how to solve it?

    1. Sorry - for these kind of details please post to the discussion groups or contact the ADN team.

      Regards,

      Kean

  22. Hi Kean, interesting topic and I am making great use of this at the moment. Instead of creating a paletteset and adding it to that is there any way to add a palette to an existing item such as Toolspace and host WPF inside of that?

    1. As far as I'm aware you can only use WPF inside a palette (or your own custom dialog... such as the recent series showing how to "dock" a window to the drawing window: keanw.com/2015/03/docking-a-wpf-window-inside-autocad-part-3.html).

      But I'm not sure what you mean by Toolspace: do you mean a tool palette?

      Kean

  23. Conrad Engelbrecht Avatar
    Conrad Engelbrecht

    Hi Kean.

    I'm experiencing a weird problem with a wpf usercontrol containing a text box in a palette. In debug mode I can type into the text box. When compiled I cannot.

    I believe it might have something to do with hosting WPF in a Windows From app such as Autocad.

    social.msdn.microso...

    According to the link above a quick fix is to add something to the likes of this.

    Window window1 = new Window();
    ElementHost.EnableModelessKeyboardInterop(window1);
    window1.Show();

    problem is. The palette only allows for adding controls or Uri's.
    ps.KeepFocus = true; also did not do the trick.

    If you have time, any ideas or settings I can change to resolve the issue would be extremely appreciated

    1. Conrad Engelbrecht Avatar
      Conrad Engelbrecht

      Here is the follow up solution.

      Palette pl = ps.AddVisual(palletName, control, true);

      Works like a charm.
      Thanks Preshen Naidoo

      1. Glad you found a solution. Thanks for letting me know.

        Kean

  24. Hi Kean!

    A bit offtop question. Is there any way to get WPF control from PaletteSet object? I have tried type cast but that doesn't work. Also Palette class has no method to get it's control. Only way I found is to store references of my controls.

    1. Kean Walmsley Avatar

      Hi Maxim,

      Yes... I think you have to store your own references, as you're doing. So it won't work for built-in or 3D party controls... although I suspect there's an OS-level way to access the controls (as there has always been with Win32/MFC).

      Regards,

      Kean

  25. Hi Kean!
    I have a problem about using WPF hosting WPF content inside an AutoCAD palette by ElementHost. I wan develop a tool for all versions of AutoCAD. The same code works fine in all versions of AutoCAD except AutoCAD2008.
    [CommandMethod("ShowExp", CommandFlags.Session)]
    public void ShowExplorer()
    {
    if (designps == null)
    {
    designps = new PaletteSet("Explorer", typeof(CommonMethod).GUID);
    designps.Style = PaletteSetStyles.Snappable | PaletteSetStyles.ShowAutoHideButton;
    ElementHost host = new ElementHost();
    host.AutoSize = true;
    host.Dock = System.Windows.Forms.DockStyle.Fill;
    host.Child = new Explorer { DataContext = ViewModels.ExplorerViewModel.Current };
    host.Refresh();
    designps.Add("Explorer", host);
    designps.Dock = DockSides.None;
    }
    designps.KeepFocus = true;
    designps.Visible = true;
    }
    I add a TreeView in the palette. I want open a CAD file when a node of the tree is clicked. When I run code using VS2008 in debug works flawlessly. When netload the .dll, all work fine at first. But palette can't change with the mouse's choice as long as click the node to open the file.
    Thanks in advance!

    1. Sorry,i run code using VS2015.

      1. I'm afraid I don't have time to provide support. Please post your question to the AutoCAD .NET forum.

        Kean

        1. OK!Thank you all the same!

Leave a Reply to Maxim Cancel reply

Your email address will not be published. Required fields are marked *