TheSwamp

Code Red => .NET => Topic started by: Keith™ on August 08, 2018, 08:52:50 PM

Title: Trouble with Palettes ...
Post by: Keith™ on August 08, 2018, 08:52:50 PM
I have been considering converting all my app forms into palettes to integrate the app better and make it more user friendly. So to that end, I added the following to my WPF Class project:

Code - C#: [Select]
  1. [CommandMethod("Test")]
  2. public void Test()
  3. {
  4.     PaletteSet ps = new PaletteSet("TestPalette");
  5.     usercontrol tc = new usercontrol();
  6.     ps.Add("Test", tc);
  7.     ps.Visible = true;
  8. }
  9.  
  10. public class usercontrol
  11. {
  12.     public usercontrol()
  13.     {
  14.     }
  15. }

It compiles just fine and loads without issue, however when calling "Test" at the command line, I am getting an error "no parameterless constructor defined for this object"

Obviously I am missing something but I just can't see it.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 08, 2018, 10:03:57 PM
Well this works .. too bad the examples don't talk about making the calling function static  :tickedoff: :tickedoff:
Code - C#: [Select]
  1. [CommandMethod("Test")]
  2. public static void Test()
  3. {
  4.     PaletteSet ps = new PaletteSet("TestPalette");
  5.     usercontrol tc = new usercontrol();
  6.     ps.Add("Test", tc);
  7.     ps.Visible = true;
  8. }
  9.  
  10. public class usercontrol
  11. {
  12.     public usercontrol()
  13.     {
  14.     }
  15. }
Title: Re: Trouble with Palettes ...
Post by: gile on August 09, 2018, 01:47:50 AM
Hi,

You can have a look to this recent message (https://forums.autodesk.com/t5/net/visual-studio-2017-c-net-wpf-within-autocad/m-p/8178142/highlight/true#M59884).
Title: Re: Trouble with Palettes ...
Post by: n.yuan on August 09, 2018, 09:44:21 AM
Well this works .. too bad the examples don't talk about making the calling function static  :tickedoff: :tickedoff:
Code - C#: [Select]
  1. [CommandMethod("Test")]
  2. public static void Test()
  3. {
  4.     PaletteSet ps = new PaletteSet("TestPalette");
  5.     usercontrol tc = new usercontrol();
  6.     ps.Add("Test", tc);
  7.     ps.Visible = true;
  8. }
  9.  

CommandMethod CAN BE either static or non-static.

You did not show all code of the class (CommandClass?) where the CommandMethod "Test" is in. However, based on error message showed in your original post, it seems the class has a constructor that requires input parameter. If this is the case, it would be problematic, or potentially bug-prone, to say the least, because in AutoCAD .NET plug-in the CommandClass (or the class that has CommandMethod defined) is not instantiated by your code (so you can choose when to "new" your class; rather it is instantiated by Autocad when the CommandMethod is executed: if the CommandMethod is static, an instance of the class is created in ApplicationConext and if the CommandMethod is non-static, an instance of the class is created per document.

So, having constructor, or having some process logic in the constructor may cause unexpected side-effect. If you need some data at class level, initialize/manipulate them either in IExtensionApplication.Initialize() (if they need to be initialized on loading); or do it in CommandMethod.

Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 09, 2018, 11:39:29 AM
Hi,

You can have a look to this recent message (https://forums.autodesk.com/t5/net/visual-studio-2017-c-net-wpf-within-autocad/m-p/8178142/highlight/true#M59884).

I've already managed to port the form to a palette by just copy/paste everything in the user form to my user control (along with all associated code). I did have about 100 items that I had to fix but it is working.

CommandMethod CAN BE either static or non-static.

That's what I thought ..

You did not show all code of the class (CommandClass?) where the CommandMethod "Test" is in. However, based on error message showed in your original post, it seems the class has a constructor that requires input parameter.

I didn't show the rest of the class because the only thing in that class are commands. This is the basic class:

Code - C#: [Select]
  1. namespace TTA
  2. {
  3.     public class Commands
  4.     {
  5.         private Commands()
  6.         {
  7.         }
  8.     }
  9. }

Now that I look at it, perhaps it was the private constructor ... but then again, that's what the wizard built ...
Title: Re: Trouble with Palettes ...
Post by: MexicanCustard on August 09, 2018, 01:01:41 PM
Make sure your giving your palettes a guid so user's preferences carry from session to session.  I also wrap my WPF palettes in an ElementHost.  That helps with the sizing.

Code - C#: [Select]
  1. var paletteSet = new PaletteSet("My Palette", new Guid("{5624342D-F672-4B0D-957E-D61127B25F76}"))
  2.                 {
  3.                     Style = PaletteSetStyles.ShowAutoHideButton | PaletteSetStyles.ShowCloseButton |
  4.                             PaletteSetStyles.Snappable,
  5.                     MinimumSize = new Size(200, 400),
  6.                     KeepFocus = false
  7.                 };
  8. var host = new ElementHost{ AutoSize = true, Dock = DockStyle.Fill };
  9. paletteSet.Add("My Palette", host);
  10. var palette = new MyPalette();
  11. host.Child = palette;
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 09, 2018, 02:55:35 PM
I've just ran into an issue that I've not seen previously.

My palette has a button that prompts for the user to select a point on the screen to insert a block. Because the palette is somewhat wide, I have it roll up when the button is clicked.
Anyway, it works fine except if the palette is docked, of course it will not roll up .. so .. using some code I stole from Autodesk University, I patched the event to undock the palette and roll it up.

However ....

Now my code seemingly skips over several lines of code and an error is generated.

The offending code:
Code - C#: [Select]
  1. if (ps.Dock.Equals(DockSides.None))
  2. {
  3.     if (ps.Style.Equals(32))
  4.     {
  5.         ps.Style = 0;
  6.     }
  7.     ps.AutoRollUp = true;
  8.     ps.Visible = false;
  9.     ps.Visible = true;
  10. }
  11. else
  12. {
  13.     ps.Dock = DockSides.None;
  14.     ps.AutoRollUp = true;
  15.     ps.Visible = false;
  16.     ps.Visible = true;
  17. }
  18.  
  19. // ...... <snip>
  20. // the values passed to the BlockJig are validated prior to this call
  21.  
  22. if (caller.Name == "btnInsert")
  23. {
  24.     BlockReference br = insert.BlockJig(bName, attData, rotAngle, rotBehavior, bScale, lay);
  25.     //Set rotation angle for all future insertions after the first one if behavior is set to 0
  26.     if (options.RotBehavior == 0 && x == 0)
  27.     {
  28.         rotAngle = br.Rotation;
  29.     }
  30.     //capture the last rotation angle in case we need to store it
  31.     options.Rotation = br.Rotation; //<-------- Program seemingly jumps to here and br is null if the palette is docked.
  32. }


There is also a timer that fires once and then turns itself off. It does the same thing as above simply setting AutoRollUp to true and cycling the visibility of the palette to effect the changes.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 09, 2018, 04:21:41 PM
I've added the GUID to the mix and now when the palette comes back up, the tabs are missing until it is minimized and opened back up.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 09, 2018, 06:31:03 PM
So long as we are discussing palettes … there are some program settings that are store in the registry. I've built a class that retrieves and stores them and it has worked well for many years. Moving to palettes causes a little bit of issue that I'm not sure how to address .. namely, writing updated values back to the registry.


I had been handling it when the user clicked the OK or Apply button in the application and reloading the values when the user accessed the form again (the forms were disposed each time they closed).


Basically the flow was:

    create a new form class
    read the settings during the load event
    do stuff
    save the settings when the user clicks Apply or OK

A palette has a load event, so I can read the settings then, do what I need to do and then what? Needlessly save the settings each time the user changes a value on the palette? Seems a bit redundant and or inefficient.
   
   
Title: Re: Trouble with Palettes ...
Post by: Atook on August 09, 2018, 09:27:20 PM
For controls that aren't tied directly to a command, I save the values to the drawing/registry OnChange.

I don't have many though, just  checkboxes, listboxes, or comboboxes. I haven't noticed the overhead yet. All actions from the user that interact with the drawing are tied to commands, so I can save then if need be.

It sounds like your OK/Apply button would be tied to a command (or something with a database transaction) that would be a good time to save settings.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 09, 2018, 09:33:27 PM
Yeah, but I've removed the buttons so it feels more like a native tool.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 09, 2018, 11:26:32 PM
I'm getting a little bit tired of having to figure out how to fix things that basically shouldn't be a problem.

I've discovered what I can only describe as a bug in palette controls behavior.

My palette currently has two controls, one with various textboxes and combo boxes and one with various checkboxes, a combo box and a numeric up/down.


Imagine a palette with two user controls, each one has a combo box on it.

Select control 1
focus the combo box
Select control 2
use the scroll button
combo box on control 1 changes
select control 1
use the scroll button
nothing happens
select control 2
use the scroll button
combo box on control 1 changes

It seems as though the control is maintaining focus even though it is not visible.

The same thing happens if I type. The text goes to the control on the hidden control.

Its infuriating!
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 10, 2018, 02:23:16 PM
I have had to resort to subclassing PaletteSet (https://forums.autodesk.com/t5/net/paletteset-and-palette-newbie-question/m-p/5525779#M43442) so I can expose functions that I need to make them behave as they should.

Title: Re: Trouble with Palettes ...
Post by: MexicanCustard on August 14, 2018, 08:59:44 AM
I have had to resort to subclassing PaletteSet (https://forums.autodesk.com/t5/net/paletteset-and-palette-newbie-question/m-p/5525779#M43442) so I can expose functions that I need to make them behave as they should.

What functionality would you need beside Show/Hide? Everything else should be handled by the palette.  I use MVVM so functionality is in the ViewModel.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 14, 2018, 01:31:58 PM
What functionality would you need beside Show/Hide? Everything else should be handled by the palette.  I use MVVM so functionality is in the ViewModel.

There is no good way to access to the underlying user control exposed functions and properties. For my purposes, this is very important.
I suppose I could hack together dozens of different global objects that represent each user control but I'd still have to handle the paletteset events to figure out which ones I need to work on at any given time.

In a paletteset you can activate a palette only by index ... if you don't know what that is (you can't really know that if your paletteset is created dynamically) then the best option is to activate the palette by name. I can do that now.

In a paletteset you cannot get the active palette. I can do that now.

Oh, and the fact that the palettes really like to maintain focus, despite losing focus on the palette itself, the controls never seem to lose focus. Microsoft says that's because the palette itself is in a different focus set than the paletteset, so technically, if you have 5 palettes in a paletteset, you could have 5 focused controls, and amazingly all or none of them may process user input at any given time. That is what initially prompted me to subclass .. my class forces the control with focus to lose focus when it is deactivated.

I've actually looked at a number of OOB software programs that utilize palettes and what I've found is they all subclass them to add functionality.

Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 14, 2018, 09:22:05 PM
I've been investigating a way to remove a palette from a PaletteSet but there doesn't appear to be any exposed methods.

I've tried a couple of different approaches and hit dead ends.


Attempt 1:
Subclass Palette and create a Dispose method
Unfortunately, Palette is marked as NotInheritable

Attempt 2:
Pass the handle of the Palette to DestroyWindow
Spy++ shows that the handle was disposed, but the PaletteSet still shows the palette as existing
Selecting the destroyed palette causes a fatal error

Attempt 3:
Pass WM_CLOSE to the palette I want to close
Palette disappears from Spy++ but is still on the screen
Selecting the palette causes a fatal error


I'm open for suggestions ...
Title: Re: Trouble with Palettes ...
Post by: n.yuan on August 15, 2018, 10:02:58 AM
Yes, one should ALWAYS subclass PaletteSet, just as we do with System.Windows.Forms.Form: we always derive our form from Form, as long as you use AutoCAD 2009 or later (prior to Acad2009, PaletteSet is a sealed class, which cannot be subclassed). I saw your post earlier in Autodesk's .NET forum, referring to an old AU class material, which gave pretty good sample code of PaletteSet. But since it was for pre Acad2009, thus did not subclass PaletteSet, thus should be considered very out-of-date.
Title: Re: Trouble with Palettes ...
Post by: MexicanCustard on August 15, 2018, 10:37:38 AM
In a paletteset you can activate a palette only by index ... if you don't know what that is (you can't really know that if your paletteset is created dynamically) then the best option is to activate the palette by name. I can do that now.

A PaletteSet is an IEnumerable, you can get a Palette by Name.
Code - C#: [Select]
  1. paletteSet.Cast<Palette>.FirstOrDefault(p => p.Name == "My Palette")
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 15, 2018, 12:34:51 PM
In a paletteset you can activate a palette only by index ... if you don't know what that is (you can't really know that if your paletteset is created dynamically) then the best option is to activate the palette by name. I can do that now.

A PaletteSet is an IEnumerable, you can get a Palette by Name.
Code - C#: [Select]
  1. paletteSet.Cast<Palette>.FirstOrDefault(p => p.Name == "My Palette")

... and that is going into my toolbox

and for whatever reason, I completely missed PaletteSet.Remove(n) ..

So I can remove transient palettes as needed ...
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 16, 2018, 12:08:20 PM
I've run into another issue and it may require reassessment of assigning transient palettes to a PaletteSet, or perhaps a workaround to prevent the problem.

Because the app is pretty large, the tools, options and various other items are located on different palettes. Depending upon which area of the program they are utilizing at the moment, the set of palettes displayed and accessible at any given time may vary, as will the number of palettes in any given paletteset, which leads me to the problem.

Scenario:
User opens app for the first time and a standard paletteset is displayed with 4 palettes.
The user clicks a button or ribbon or changes a combobox value that displays a transient palette. However it is we got there a new transient palette is added to the paletteset (created only when needed and disposed after it loses focus)
Without closing the paletteset and without navigating away from the transient palette, the user closes AutoCAD.
The next time the user tries to open AutoCAD it crashes.

Cause:
When using a GUID to identify a paletteset (and thus allow AutoCAD to remember the last settings) upon restarting AutoCAD, an attempt is made to activate the now non-existant palette. Error handling does not catch the error.

Recovery Fix:
One of the following:
Find the user profile in %userprofile%\AppData\Roaming\Autodesk\ .... and edit the XML to either remove the paletteset or change the active palette value to an index that exists i.e. 0
OR
Delete the user profile

I've tried catching the Finalize event for the paletteset and forcing the active palette value to 0 but apparently the settings are saved before palette is disposed so ...

Aside from not having transient palettes anyone have a good idea to solve this issue? Surely I can't be the only person who has had this issue before.

Title: Re: Trouble with Palettes ...
Post by: Atook on August 16, 2018, 01:40:10 PM
Keith, I have to say, my first thought was, if it's transient, maybe it should be a form.

Then I looked at my own code, and I have a transient-ish palette (brought up via command from another palette). It lives in it's own paletteset as it's larger than the other palettes I show, and is meant to go away when the user is done with it. Not sure if I have the same issue as you, but haven't run into it yet.

Can you create the palette, and set the control.visible property to false instead of disposing it? (and disable the logic in it based on it's visibility)

Another option might be tapping into the App.BeginQuit() event, and closing your transient palette there.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 16, 2018, 04:30:03 PM
Like I said, I am beginning to rethink it ...
Title: Re: Trouble with Palettes ...
Post by: MexicanCustard on August 17, 2018, 07:22:30 AM
Keith, I have to say, my first thought was, if it's transient, maybe it should be a form.

Agreed. Palettes are best for static use. Letting the user show and hide them as needed.  If I had nested windows I wouldn't try and put them in a palette.  Can the transient data just be shown/hidden in the parent control?  I have some palettes where depending on the users choices additional data is shown on the same control.
Title: Re: Trouble with Palettes ...
Post by: MexicanCustard on August 17, 2018, 07:23:40 AM
Or can you just swap out the control on the same palette and let the user navigate back and forth?
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 17, 2018, 07:58:11 AM
For now I'm going to leave the palette and reconfigure one other. I'll eventually work it out, it's just not today.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 17, 2018, 08:03:01 AM
I still have some sizing issues though. The tabs don't show the first time it's called until, a)the palette is rolled up and expanded or b) the palette is resized larger that the minimum size.

And the palette does not adhere to minimum size when docked. It can be resized smaller than the minimum size.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 17, 2018, 11:03:42 AM
Is it possible to utilize the same type control for displaying/editing data as AutoCAD uses for their palettes? I am thinking the one like on the Modify palette with various minimizable/maximizable sections that list the editable values and have controls for changing said values.

Spy++ lists it as a subclassed ListView and I see there is an ExListView control listed, but so far I have been unable to instantiate it.


*edited to correct class name
Title: Re: Trouble with Palettes ...
Post by: MexicanCustard on August 21, 2018, 07:49:05 AM
I still have some sizing issues though. The tabs don't show the first time it's called until, a)the palette is rolled up and expanded or b) the palette is resized larger that the minimum size.

And the palette does not adhere to minimum size when docked. It can be resized smaller than the minimum size.

This is why I wrap the WPF Control inside an ElementHost.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 21, 2018, 08:15:17 AM
Care to share an example?
With this method can I use an existing control and just wrap the control in my subclass pallet set?
Title: Re: Trouble with Palettes ...
Post by: MexicanCustard on August 21, 2018, 10:43:45 AM
Check out my first post in this thread.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 21, 2018, 04:32:07 PM
Ah well … not using WPF controls and I'm not thinking a rewrite is in order
I ended up wrapping them in a Panel and using the same technique and amazingly it worked, although I had to dockfill the Usercontrol in the panel too.


So far so good ...
Title: Re: Trouble with Palettes ...
Post by: MexicanCustard on August 22, 2018, 07:33:21 AM
Ah well … not using WPF controls

Now I see why you've had some of the issues you've had and the need to subclass.

WPF and the MVVM architecture is the only way to go in C# for desktop UI. Time to let the past go and move over.
Title: Re: Trouble with Palettes ...
Post by: Keith™ on August 22, 2018, 08:31:15 AM
Ah well … not using WPF controls

Now I see why you've had some of the issues you've had and the need to subclass.

WPF and the MVVM architecture is the only way to go in C# for desktop UI. Time to let the past go and move over.

If there weren't already 4 years of development in this project, I'd be happy to do that, but alas, I can't jeopardize the release date.