Phillip Trelford's Array

POKE 36879,255

Silverlight Context Menu Scrolling

Does your application have long context menus that get clipped inside small windows?

First ask yourself is this the right user interface.

If so then one solution is to add a vertical scroll bar automagically to the context menu.

IntelliSense in Visual Studio uses a maximum size constant for the context menu to cope with the inordinate number of members on many classes (over 150 members on a Button):

button_intellisense

Another option, when screen space is at a premium and windows are small, is to peg the maximum size of the context menu to the window height:

ContextMenu

The screen shot above shows a Silverlight out-of-browser window where the left context menu is clipped, the right simply employs a scroll bar to make all menu items accessible.

Clipping is the default behaviour in the Silverlight Toolkit’s ContextMenu control. In the Silverlight Multi-Window Controls project I’ve made automatic scrolling the default.

Adding Scrolling to the Silverlight Toolkit ContextMenu

The first step is to add a ScrollViewer control to the Silverlight Toolkit’s ContextMenu style which coincidentally already contains one that only needs be un-commented:

    <!--<ScrollViewer
        Margin="1,0,1,0"
        Grid.ColumnSpan="2"
        BorderThickness="0"
        VerticalScrollBarVisibility="Auto">—>
            <ItemsPresenter Margin="{TemplateBinding Padding}"/>
    <!--</ScrollViewer>-->

To set a constant maximum height, simply add a MaxHeight property value.

Pegging the maximum height of the context menu to the window’s height is a little more involved. The mechanism I came up with was to make the ScrollViewer’s parent control (a Grid) track the height of the window:

    <controls:ResizingGrid>
        <ScrollViewer
            Margin="1,0,1,0"
            Grid.ColumnSpan="2"
            BorderThickness="0"
            VerticalScrollBarVisibility="Auto">
            <ItemsPresenter Margin="{TemplateBinding Padding}" />
        </ScrollViewer>
    </controls:ResizingGrid>

The tracking is implemented in a ResizingGrid class that inherits from Grid:

public class ResizingGrid : Grid
{
    FrameworkElement _rootVisual;

    public ResizingGrid()
        : base()
    {
        this.Loaded += GridLoaded;
        this.Unloaded += GridUnloaded;
    }

    void GridLoaded(object sender, RoutedEventArgs e)
    {
        if (Application.Current.IsRunningOutOfBrowser &&
            Application.Current.HasElevatedPermissions)
        {
            var window = Window.GetWindow(sender as DependencyObject);
            _rootVisual = window.Content;
        }
        else
        {
            _rootVisual = Application.Current.RootVisual as FrameworkElement;
        }
        _rootVisual.SizeChanged += ContentSizeChanged;
        SetHeight(_rootVisual.ActualHeight);
    }

    void GridUnloaded(object sender, RoutedEventArgs e)
    {
        _rootVisual.SizeChanged -= ContentSizeChanged;
    }

    void ContentSizeChanged(object sender, SizeChangedEventArgs e)
    {
        SetHeight(e.NewSize.Height);
    }

    private void SetHeight(double windowHeight)
    {
        var size = this.MeasureOverride(
                          new Size(double.MaxValue,double.MaxValue));
        var contentHeight = size.Height;
        Height =
            windowHeight < contentHeight
            ? windowHeight
            : Double.NaN;
    }
}

Adding Scrolling to Telerik’s Silverlight RadContextMenu

Telerik have a “How To” page on adding a ScrollViewer with a MaxHeight to their context menu control: Add Scrolling to RadContextMenu.

It requires Expression Blend, which is currently available free in preview form:

There is very little difference between the Silverlight Toolkit and Telerik context menu implementations, so the style can be adapted for automatic resizing in the same way as the Silverlight Toolkit context menu was by using the ResizingGrid:

    <this:ResizingGrid>
        <ScrollViewer VerticalScrollBarVisibility="Auto">
        <ItemsPresenter Margin="1"/>
        </ScrollViewer>
    </this:ResizingGrid>

That’s it! Happy right-clicking.

Comments are closed