locked
Customizable toolbars for my WinForms applications. RRS feed

  • Question

  • I'm working on a project involving a bunch of Windows Forms applications and I'm planning to enable the users to customize the applications' toolbars and menus (ie: adding and removing entries, changing the text and icons for such entries, and so on). While the flexibility of the ToolStrip and ToolStripContainer controls provided by the .NET Framework will be quite useful for this, there are some issues I need to solve yet. Before I start reinventing the wheel on this, I would like to know if the Framework already provides a solution for any of these issues.
    I have checked the ToolStrip Customizing sample at http://msdn.microsoft.com/en-us/library/ms181004.aspx, but it only deals with the almost trivial task of adding items to and removing them from the toolbars.
    What I would need to achieve is:
    1. Adding new toolbars: I hope this will be quite easy, involving just the creation of a ToolStrip object and adding it to the ToolStripContainer in the form.
    2. Drag&Drop: the user should be able to drag buttons (and other controls) around the toolbars, including moving them between different toolbars, or dragging them out to remove them. It would also be useful to be able to drag "commands" from the customization dialog and drop them in the toolbar (or menu) to create a new button. The AllowItemReorder property from the ToolStrip control almost achieves this, but has three serious limitations: it doesn't allow dragging a control outside of the toolbar, so I cannot make my program to remove the button when this happens (it cannot happen at all); the Alt key must be pressed to move the controls (maybe this might be worked around by "pressing" the key from the KeyDown event's handler when the form is in "Customization mode"); and this option prevents me from setting the AllowDrop property to true, so the user can't drop a button from the customization dialog to the toolbar to add the button. Is there any work around for these limitations, or should I forget about AllowItemReorder and implement these functionalities from scratch through the mouse and drag events?
    3. Locking toolbars: once the users start customizing their toolbars, they probably won't want everything to mess up as soon as they accidentally drag something out of place. For this reason, I'd like to add a "Lock toolbars" option somewhere which would prevent these accidents. In order to implement it, is there any way to "block" the current position of controls within a ToolStripContainer?
    4. Floating toolbars: as simple as that: I'd like the users to be able to make some (or all, or none) of the toolbars to "float", so they can be moved freely around the main form instead of having them docked on the edges. Of course, the user should also be able to dock the toolbars again. Any idea/suggestion about how to achieve this would be appreciated.
    5. Serialization: that's the juicy part: if everytime the user changes the UI and restarts the application everything is in the default place, then all efforts until now have been quite useless. I need a way to persist these customizations. This involves two tasks: saving the current state of the toolbars to a file and loading that state from such file again on startup. The biggest issue here is about saving (and loading) the binding of each control to each functionality, like the binding of the "Save" button's click event to the "Save" function. To make it more challenging, the applications will be including support for some scripting and addons (that's an independent topic, but has some implications here): the user should be able to bind UI elements (basically toolbar controls and menu entries) to the functionalities provided by these addons. This means that the application's code won't know beforehand which "commands" are available, and will need to figure them out "on the fly". The only idea I currently have to handle this is to use reflection to build a list of suitable methods exposed by both the application and every addon (by suitable I mean they should take no arguments, or only arguments of simple types such as strings or numeric types), and save the "commands" as strings with a "call-like" syntax. For example, the command for the "Copy" button might be saved as the string "ClipboardCopy()". Only literal arguments would be allowed, so parsing these strings would be almost trivial. If the users need a button with more elaborated logics, they will be able to build a macro, which never takes arguments, and bind the button to RunMacro("name_of_the_macro"). If somebody has any better ideas or any suggestion about how to improve this approach, please let me know.
    If someone is wondering, the goal I'm trying to achieve is quite comparable to Microsoft Office's toolbar customizability (at least like in versions 97 through 2003, before toolbars got replaced by ribbons in MSOS 2007), Visual Studio itself, and some other programs. If I didn't mention this earlier in the post is to avoid confusion: all the attempts to find resources about this topic on the web or on this forums lead to pages and posts about .Net-Office interoperability or, in some cases, web browser toolbars, which are completelly unrelated to what I'm trying to do. I just want to implement a feature which became widely known with its inclusion on Office 97; but the application will not be interacting with Office at all (and it should run on systems that do not have any Office software installed, currently it runs even on Kubuntu upon Mono).

    Thanks in advance for any help you provide.
    Sunday, June 15, 2008 7:58 PM

Answers

  • Well, I've started "reinventing the wheel" on my own and have already solved many of the issues:
    For Drag&Drop, my "Customize dialog" uses an actual toolbar as the "command repository", so commnads can be dragged from there to other toolbars (and the repository will react to the ItemAdded and ItemRemoved events recreating the original list of commands, so the repository itself always contains all the available commands, without repetition). I haven't been able to achieve the drag out to remove functionality, but the dialog includes another "toolbar" that acts as a trash (and it calls Items.Clear() on ItemAdded, so efectively removes the "commands"). The part I expected to be most trivial, simulating the Alt key while in "customization mode", is still giving problems: currently my prototypes work fine, but the Alt key needs to be pressed to move the buttons somewhere (maybe this is even a good thing, since it provides some safety against accidental dragging).
    Locking toolbars: GripStyle = Hidden; AllowItemReorder = false; AllowDrop = false; and voilà, the bar is locked Big Smile
    To unlock it, GripStyle = Visible; AllowItemReorder = true; and there you go. It took a while before I found the GripStyle property, but it works great. In addition, since it hides the "grip" of the toolbar, the user has a visual clue that the bars are locked.
    Floating toolbars: that was a bit tricky, but I finally got it solved: "Undocking" the bar causes it to: 1) Save it's current Parent property in a private field; 2) Creating a new form on the fly, with FormBorderStyle = SizableToolWindow (and Text = the bar's own Text); 3) Remove itself from its Parent, and add itself to the new form; and 4) Show the new form. Docking again the bar reverses the process: 1) Removes it from the form and adds itself to the saved "ex-Parent" control; 2) Closes the form; and 3) sets that "ex-Parent" field to null, so it can be checked to know whether the bar is docked or floating at any given moment.
    Persisting the bars: that's still work in progress, but it's taking long more due to the raw ammount of code needed than to that code's complexity. It would be quite verbose to go into details here, but as a summary: There is an abstract class, CommandBase, which holds the "command" as a String, as well as the icon and text for the toolbar item. It has an abstract readonly property (which is implemented by inheritors) named ToolStripItem, of the obvious type, which produces the ToolStripItem to represent that command in the toolbars. In order to produce the eventhandler for the ToolStripItem, the class relies on a protected field named "Resolver", whose type is the interface IToolbarCommandResolver. That interface is quite simple, and its hearth is the void ResolveCommand(String Command); method. The application can now provide a resolver for its own commands, and each addon should be able to provide a resolver implementation if they "want" to provide commands for the toolbars.

    So, of all the original issues, the only one that is still pending is simulating the state of the Alt key. I've tried calling the OnKeyDown and OnKeyUp methods (I'm implementing this as a derived class); but it didn't do the job. I guess the ToolStrip is checking the state of the key when it detects mouse events, rather than keeping track of that state from the key events (it makes sense, since the KeyDown could happen in some other control and, as long as the key is held, reordering works fine). It would be idea if I could override the code that checks the Alt state with my own check (which would check whether the bars are in "customization mode" or not). An alternative would be messing up with Windows itself, and cause it think that the key is pressed when I want it to be, but this would most likely bring nasty side effects. The final option, if everything else fails, would be to look at the Mono Project's source for this control, and see how have they solved this. However, if somebody has any idea on how to emulate the Alt key state, it would be really appreciated.

    Once I get this finnished, polished, and documented, I guess I'll be publishing it under some open source license, so other people can benefit of it without having to pay huge ammounts of money for proprietary implementations.
    Tuesday, June 17, 2008 5:02 PM
  •  

    Hi herenvardo,

    Based on my understanding, what you want to do is to simulate the ALT key pressing when the ToolStrip is in customization mode, right?  If so, you can use the keybd_event API to change the state of the key board. Here is a sample for your information:

    Code Snippet

            [DllImport("user32.dll")]

            public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);

     

            const int VK_MENU = 0x12;

            const uint KEYEVENTF_KEYUP = 0x2;

            bool altPressed = false;

     

            private void button1_Click(object sender, EventArgs e)

            {

                if (!altPressed)

                {

                    keybd_event((byte)VK_MENU, 0, 0, 0);

                }

                else

                {

                    keybd_event((byte)VK_MENU, 0, KEYEVENTF_KEYUP, 0);

                }

                altPressed = !altPressed;

            }

     

            private void button2_Click(object sender, EventArgs e)

            {

                MessageBox.Show(Control.ModifierKeys.ToString());

            }

     

    If you just want to check if the ALT key is in pressed state, you can use the Control.ModifierKeys to get a value indicating which of the modifier keys (SHIFT, CTRL, and ALT) is in a pressed state.

    Hope this helps.
    Best regards.
    Rong-Chun Zhang

    Windows Forms General FAQs
    Windows Forms Data Controls and Databinding FAQs

    Friday, June 20, 2008 9:43 AM

All replies

  • There is no supports for this in the .NET framework at all.  Adding this is highly non-trivial.  Most library vendors have some kind of wizbang toolbar component that tries to mimic the Microsoft toolbars, check them out.
    Tuesday, June 17, 2008 12:34 AM
  • Well, I've started "reinventing the wheel" on my own and have already solved many of the issues:
    For Drag&Drop, my "Customize dialog" uses an actual toolbar as the "command repository", so commnads can be dragged from there to other toolbars (and the repository will react to the ItemAdded and ItemRemoved events recreating the original list of commands, so the repository itself always contains all the available commands, without repetition). I haven't been able to achieve the drag out to remove functionality, but the dialog includes another "toolbar" that acts as a trash (and it calls Items.Clear() on ItemAdded, so efectively removes the "commands"). The part I expected to be most trivial, simulating the Alt key while in "customization mode", is still giving problems: currently my prototypes work fine, but the Alt key needs to be pressed to move the buttons somewhere (maybe this is even a good thing, since it provides some safety against accidental dragging).
    Locking toolbars: GripStyle = Hidden; AllowItemReorder = false; AllowDrop = false; and voilà, the bar is locked Big Smile
    To unlock it, GripStyle = Visible; AllowItemReorder = true; and there you go. It took a while before I found the GripStyle property, but it works great. In addition, since it hides the "grip" of the toolbar, the user has a visual clue that the bars are locked.
    Floating toolbars: that was a bit tricky, but I finally got it solved: "Undocking" the bar causes it to: 1) Save it's current Parent property in a private field; 2) Creating a new form on the fly, with FormBorderStyle = SizableToolWindow (and Text = the bar's own Text); 3) Remove itself from its Parent, and add itself to the new form; and 4) Show the new form. Docking again the bar reverses the process: 1) Removes it from the form and adds itself to the saved "ex-Parent" control; 2) Closes the form; and 3) sets that "ex-Parent" field to null, so it can be checked to know whether the bar is docked or floating at any given moment.
    Persisting the bars: that's still work in progress, but it's taking long more due to the raw ammount of code needed than to that code's complexity. It would be quite verbose to go into details here, but as a summary: There is an abstract class, CommandBase, which holds the "command" as a String, as well as the icon and text for the toolbar item. It has an abstract readonly property (which is implemented by inheritors) named ToolStripItem, of the obvious type, which produces the ToolStripItem to represent that command in the toolbars. In order to produce the eventhandler for the ToolStripItem, the class relies on a protected field named "Resolver", whose type is the interface IToolbarCommandResolver. That interface is quite simple, and its hearth is the void ResolveCommand(String Command); method. The application can now provide a resolver for its own commands, and each addon should be able to provide a resolver implementation if they "want" to provide commands for the toolbars.

    So, of all the original issues, the only one that is still pending is simulating the state of the Alt key. I've tried calling the OnKeyDown and OnKeyUp methods (I'm implementing this as a derived class); but it didn't do the job. I guess the ToolStrip is checking the state of the key when it detects mouse events, rather than keeping track of that state from the key events (it makes sense, since the KeyDown could happen in some other control and, as long as the key is held, reordering works fine). It would be idea if I could override the code that checks the Alt state with my own check (which would check whether the bars are in "customization mode" or not). An alternative would be messing up with Windows itself, and cause it think that the key is pressed when I want it to be, but this would most likely bring nasty side effects. The final option, if everything else fails, would be to look at the Mono Project's source for this control, and see how have they solved this. However, if somebody has any idea on how to emulate the Alt key state, it would be really appreciated.

    Once I get this finnished, polished, and documented, I guess I'll be publishing it under some open source license, so other people can benefit of it without having to pay huge ammounts of money for proprietary implementations.
    Tuesday, June 17, 2008 5:02 PM
  •  

    Hi herenvardo,

    Based on my understanding, what you want to do is to simulate the ALT key pressing when the ToolStrip is in customization mode, right?  If so, you can use the keybd_event API to change the state of the key board. Here is a sample for your information:

    Code Snippet

            [DllImport("user32.dll")]

            public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);

     

            const int VK_MENU = 0x12;

            const uint KEYEVENTF_KEYUP = 0x2;

            bool altPressed = false;

     

            private void button1_Click(object sender, EventArgs e)

            {

                if (!altPressed)

                {

                    keybd_event((byte)VK_MENU, 0, 0, 0);

                }

                else

                {

                    keybd_event((byte)VK_MENU, 0, KEYEVENTF_KEYUP, 0);

                }

                altPressed = !altPressed;

            }

     

            private void button2_Click(object sender, EventArgs e)

            {

                MessageBox.Show(Control.ModifierKeys.ToString());

            }

     

    If you just want to check if the ALT key is in pressed state, you can use the Control.ModifierKeys to get a value indicating which of the modifier keys (SHIFT, CTRL, and ALT) is in a pressed state.

    Hope this helps.
    Best regards.
    Rong-Chun Zhang

    Windows Forms General FAQs
    Windows Forms Data Controls and Databinding FAQs

    Friday, June 20, 2008 9:43 AM