Divelements Community
Customer discussion of all Divelements products

SandBar Best Practices

Latest post 06-19-2005 13:19 by Tim Dawson. 3 replies.
  • 06-19-2005 13:19

    SandBar Best Practices

    Locked |Contact
    This thread will be used to provide recommendations on the best way to use SandBar in your application.
  • 06-19-2005 13:22 In reply to

    Control Authors: Using SandBar Context Menus

    Locked |Contact
    If you are a control author and want great context menus within your control, SandBar gives you the functionality to do so in a fast, lightweight manner. The two most important things to consider about your custom context menu are keyboard shortcuts and when to display the menu. The rest is as easy as programmatically building the menu structure starting with your toplevel ContextMenuBarItem instance.

    [b]Keyboard Shortcuts[/b]

    When writing a control, the last thing you want is the main MenuBar class listening for shortcuts throughout the whole form your control ends up on. In order to get the most efficient response, you need to give SandBar a little help by passing it valid shortcuts when you receive them.

    The key to this level of keyboard shortcut integration is the ShortcutListener class. This is used internally by SandBar but you can use it too, to help with processing shortcuts correctly. Here are the points to remember when integrating keyboard shortcut detection:

    Create a ShortcutListener (in the constructor)
    Create your ContextMenuBarItem (and populate it)
    Assign your context menu with the ShortcutListener's UpdateAcceleratorTable method.
    Override your ProcessCmdKey method and pass keyboard commands to your ShortcutListener.
    Dispose of your ShortcutListener in your overridden Dispose method.

    [b]Displaying the Menu[/b]

    The most common mistake made here is showing the context menu manually in the MouseUp event of your control. This can cause all sorts of problems as it is not the place Windows expects a context menu to be shown. Instead, Windows sends a special message to your control when its context menu should be displayed. Remember that a right-click is not the only way this can be triggered - users can also press the ContextMenu button on their keyboard.

    The SandBar MenuBar class contains helpers that can listen for the correct message and show your context menu when appropriate. You therefore need to create an instance of MenuBar, add your context menu to it and associate the context menu with your control. SandBar will take care of showing it at the right time.

    It is important to note that although you are creating a MenuBar instance, because it's never parented to a control its handle is never created. Therefore most of the resources normally associated with a MenuBar are avoided and you are left with a lightweight solution. Remember to dispose of the MenuBar in your overridden Dispose method.

    [b]Conclusion[/b]

    You can now use two SandBar classes to give yourself all the context menu functionality you need in your own controls, in a solid and lightweight manner. Here's a skeleton UserControl that illustrates this information, but bear in mind it needs to be extended with some real content before it will function properly (i.e. it needs to accept focus).

    [code]
    using System;
    using System.Windows.Forms;
    using TD.SandBar;

    namespace WindowsApplication23
    {
    public class UserControl1 : System.Windows.Forms.UserControl
    {
    // SandBar integration
    private ShortcutListener shortcuts = null;
    private MenuBar menuBar = null;
    private ContextMenuBarItem contextMenu = null;

    public UserControl1()
    {
    // Create shortcut listener and menubar
    shortcuts = new ShortcutListener();
    menuBar = new MenuBar();

    // Create sample context menu
    contextMenu = new ContextMenuBarItem();
    contextMenu.MenuItems.AddRange(new MenuButtonItem[] { new MenuButtonItem("Sample Item 1"), new MenuButtonItem("Sample Item 2"), new MenuButtonItem("Sample Item 3") });
    contextMenu.MenuItems[1].Shortcut = Shortcut.CtrlJ;

    // Update shortcut listener with our finished context menu
    shortcuts.UpdateAcceleratorTable(new TopLevelMenuItemBase[] { contextMenu });

    // Associate our context menu with this control
    menuBar.Buttons.Add(contextMenu);
    menuBar.SetSandBarMenu(this, contextMenu);
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
    // Send this to SandBar and return true if we activated a menu item
    if(shortcuts.ShortcutActivated(keyData))
    return true;

    return base.ProcessCmdKey(ref msg, keyData);
    }

    protected override void Dispose( bool disposing )
    {
    if (disposing)
    {
    // Clean up the shortcut listener
    shortcuts.Dispose();

    // Clean up the context menu association
    menuBar.SetSandBarMenu(this, null);
    menuBar.Dispose();
    }

    base.Dispose(disposing);
    }

    }
    }
    [/code]
  • 06-19-2005 13:26 In reply to

    Using Images with SandBar

    Locked |Contact
    SandBar supports pretty much every way to associate picture data with toolbar and menu items, but which method you choose can depend on a number of factors. In this entry I will discuss the pros and cons of each method, along with my recommendation.

    [b]ImageLists[/b]

    Pros. The ImageList scenario involves associating your toolbar with a win32 ImageList control and then associating each toolbar item thereafter with an image offset within the images collection. This makes it easy to visually select the image you want for each component. This is also the easiest way of sharing images between toolbars and menus.

    Cons. At present, it is impossible to add high-quality 32bit images to an ImageList control without their alpha channel being corrupted due to bugs in the framework. This has been addressed in .NET 2.0.

    [b]Images[/b]

    Pros. The Image property allows direct assignment of an image to a toolbar or menu item. This means it is never held in a win32 ImageList and therefore will be preserved in its original state. This is the recommended way of using images with SandBar, as you can obtain very high quality 32bit images similar to those used in Microsoft Office 2003 to give your application truly distinctive and attractive toolbars and menus.

    Also, in Visual Studio 2005 you can use this property to share images between controls in a form and even through your application with the use of resource files.

    Cons. Not so easy to make efficient use of resources. Prior to Visual Studio 2005 there is no way to have a centralised repository of image data used throughout your application that works in the designer, so in most cases you will end up associating the same image with a menu item and its associated toolbar item. This will result in the image being stored twice.

    [b]Icon and IconSize[/b]

    Pros. The Icon property allows you to associate Icons with each component. Icon files can contain more than one size of icon. Therefore by associating an icon file with both a 32x32 and 16x16 version of an image with your toolbar item, you can give the user the option to have large or small icons on your toolbar by looping through setting the IconSize property of each item. Also under Windows XP, your icons can contain alpha channels.

    Cons. Because icons usually contain more than one size image, this can be a resource intensive in terms of the size of your assemblies. This method also shares the cons of the Image method.
  • 06-19-2005 13:28 In reply to

    Handling Clipboard Shortcuts

    Locked |Contact
    With great power comes great responsibility. When you assign a keyboard shortcut to a SandBar menu item, every press of that keyboard shortcut will trigger that menu item's Activate instead of whatever it would have done previously. The exception is when the focus is contained in a well-written native .net control, in which case SandBar will pass the keystroke to that control if it indicates it wants to process it. Unfortunately most of the out-of-the-box .net controls are merely wrappers for win32 controls so do not have this rich functionality.

    Clipboard shortcuts are the most common place for application authors to forget about handling every situation gracefully. Depending on the complexity of your form one of any number of different controls could have the focus when a keyboard shortcut is activated. Those controls could be .net or win32. For instance, what if the user is midway through editing text in a label in a TreeView or ListView control. If they decide to use your Edit menu to cut some text, they will expect it to work. A great deal of .net programs will fall short at this point.

    There is no good managed way to deal with clipboard commands and focused controls, unfortunately. When integrating proper clipboard support you will need to use a little platform invoke to make sure things are done smoothly and in a manner the user would expect. Here's the flow of what happens on a clipboard shortcut press:

    User activates keyboard shortcut (Ctrl-X)
    SandBar catches the shortcut and asks the active control whether it would like to handle it (if yes, finish here)
    SandBar activates the menu item that corresponds to the shortcut
    Your application queries the focus and attempts to find a .net control that corresponds
    Your application calls a method on the control if it's of a known type OR as a last resort sends the WM_CUT message

    Using this system you can ensure the greatest compatibility between your menus and the rest of Windows. In a large MDI application you might choose to pass the message on to your active document (which might implement an IClipboard interface) for special processing. What's important is that if you cannot handle the shortcut specially, you pass the message on by sending a window message to the focused control.

    Here is a snippet of code that should be used as a skeleton framework for handling clipboard shortcut activations. It handles win32 controls as well as extended support for .net TextBox and RichTextBox controls.

    [code]
    [DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.Winapi)]
    private static extern IntPtr GetFocus();
    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    private static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);

    private const int WM_CUT = 0x300;
    private const int WM_COPY = 0x301;
    private const int WM_PASTE = 0x302;
    private const int WM_UNDO = 0x304;

    private void mnuEditCut_Activate(object sender, System.EventArgs e)
    {
    Control focusedControl = GetFocusedControl();
    if (focusedControl is TextBoxBase)
    ((TextBoxBase)focusedControl).Cut();
    else if (focusedControl is MyControlType)
    {
    // Special application processing for this event
    }
    else
    SendMessage(new HandleRef(this, GetFocus()), WM_CUT, 0, 0);
    }

    private void mnuEditCopy_Activate(object sender, System.EventArgs e)
    {
    Control focusedControl = GetFocusedControl();
    if (focusedControl is TextBoxBase)
    ((TextBoxBase)focusedControl).Copy();
    else if (focusedControl is MyControlType)
    {
    // Special application processing for this event
    }
    else
    SendMessage(new HandleRef(this, GetFocus()), WM_COPY, 0, 0);
    }

    private void mnuEditPaste_Activate(object sender, System.EventArgs e)
    {
    Control focusedControl = GetFocusedControl();
    if (focusedControl is TextBoxBase)
    ((TextBoxBase)focusedControl).Paste();
    else if (focusedControl is MyControlType)
    {
    // Special application processing for this event
    }
    else
    SendMessage(new HandleRef(this, GetFocus()), WM_PASTE, 0, 0);
    }

    private void mnuEditUndo_Activate(object sender, System.EventArgs e)
    {
    Control focusedControl = GetFocusedControl();
    if (focusedControl is TextBoxBase)
    ((TextBoxBase)focusedControl).Undo();
    else if (focusedControl is MyControlType)
    {
    // Special application processing for this event
    }
    else
    SendMessage(new HandleRef(this, GetFocus()), WM_UNDO, 0, 0);
    }

    private void mnuEditRedo_Activate(object sender, System.EventArgs e)
    {
    Control focusedControl = GetFocusedControl();
    if (focusedControl is RichTextBox)
    ((RichTextBox)focusedControl).Redo();
    else if (focusedControl is MyControlType)
    {
    // Special application processing for this event
    }
    else
    SendMessage(new HandleRef(this, GetFocus()), WM_UNDO, 0, 0); // Win32 has no WM_REDO, so we'll just send another WM_UNDO
    }

    private Control GetFocusedControl()
    {
    // Try and find the .net control instance with the focus
    IntPtr focus = GetFocus();
    if (focus == IntPtr.Zero)
    return null;
    else
    return Control.FromHandle(focus);
    }
    [/code]
Page 1 of 1 (4 items) | RSS
Copyright © 2008 Divelements Limited
Powered by Community Server (Commercial Edition), by Telligent Systems