In our previous blog about RadSpreadsheet we decided to do a quick warm up and show you how to plug the commands of RadSpreadsheet to a RadToolBar. Now it’s time to dive into RadSpreadsheet and learn tips and tricks about the other extension points of the control. Particularly, this post shows how to construct a context menu according to your needs and also, add items that execute custom commands. The context menu we will create will have two menu items for the copy and paste built-in commands and also a Highlight menu item for our custom command:

Taking a closer look at RadSpreadsheet reveals that, in fact, it does not have a built-in context menu. So, if you drop the control from the toolbox and run your app, right-clicking on the sheet’s cells will not display a menu. Why did we do it that way? Well, because it is very easy to build your own custom menu that contains only the items you need. Remember command descriptors from the RadSpreadsheet Tips and Tricks: RadToolBar in RadSpreadsheet – No Problem blog post? In case you don’t, command descriptors allow you to construct a context menu containing any combination of all available spreadsheet commands effortlessly! Each descriptor exposes a command and an IsEnabled property that indicates whether the command is currently available. Having said that, command descriptors are a powerful mechanism that allows you to bind any UI to a predefined spreadsheet command very easily.

For example, the following snippet sets up a context menu with two items: copy and paste. Of course, if you want to stick to the standard context menu, you can always get the code from the SDK example.

<telerik:RadSpreadsheet Grid.Row="1" x:Name="radSpreadsheet" DataContext="{Binding Path=CommandDescriptors, ElementName=radSpreadsheet}" >
    <telerik:RadSpreadsheet.WorksheetEditorContextMenu>
        <telerik:RadContextMenu>
            <telerik:RadMenuItem Header="Copy"
                                 Command="{Binding Path=Copy.Command}"
                                 Visibility="{Binding Path=Copy.IsEnabled, Mode=TwoWay, Converter={StaticResource BoolToVisibilityValueConverter}}">
                <telerik:RadMenuItem.Icon>
                    <Image telerik:IconManager.IconPropertyName="Source"
                           telerik:IconManager.IconSource="/Telerik.Windows.Controls.Spreadsheet;component/Images/Light/16/copy.png" Width="16"/>
                </telerik:RadMenuItem.Icon>
            </telerik:RadMenuItem>
 
            <telerik:RadMenuItem Header="Paste"
                                 Command="{Binding Path=Paste.Command}"
                                 Visibility="{Binding Path=Paste.IsEnabled, Mode=TwoWay, Converter={StaticResource BoolToVisibilityValueConverter}}">
                <telerik:RadMenuItem.Icon>
                    <Image telerik:IconManager.IconPropertyName="Source" telerik:IconManager.IconSource="/Telerik.Windows.Controls.Spreadsheet;component/Images/Light/16/pasteNormal.png" Width="16"/>
                </telerik:RadMenuItem.Icon>
            </telerik:RadMenuItem>
        </telerik:RadContextMenu>
    </telerik:RadSpreadsheet.WorksheetEditorContextMenu>
</telerik:RadSpreadsheet>

Note that the data context of the spreadsheet is set to the CommandDescriptors property. Once descriptors are set as data context each of the RadMenuItems in the RadContextMenu can bind the Command and IsEnabled properties to any descriptor.

So, adding items that execute a command provided by the RadSpreadsheet API is seamless. That said, let’s see what happens when you would like to plug a custom command. For example, you may want to allow the user to highlight selected cells as special, which changes their background color to yellow. The highlight option should not be available if the currently selected cells have been already highlighted.

Let us start by specifying a command that invalidates whenever the selection changes. The following class inherits DelegateCommand class from the Telerik.Windows.Controls dll. The implementation invalidates the command’s CanExecute property whenever the selection of the current WorksheetEditor changes.

public class SelectionDependentCommand : DelegateCommand
{
    private readonly RadSpreadsheet radSpreadsheet;
    private RadWorksheetEditor worksheetEditor;
 
    public SelectionDependentCommand(RadSpreadsheet radSpreadsheet, Action<object> action, Predicate<object> predicate)
        : base(action, predicate)
    {
        this.radSpreadsheet = radSpreadsheet;
 
        this.radSpreadsheet.ActiveSheetEditorChanged += this.RadSpreadsheetActiveSheetEditorChanged;
    }
 
    private void RadSpreadsheetActiveSheetEditorChanged(object sender, EventArgs e)
    {
        if (this.worksheetEditor != null)
        {
            this.worksheetEditor.Selection.SelectionChanged -= this.Selection_SelectionChanged;
        }
 
        this.worksheetEditor = this.radSpreadsheet.ActiveWorksheetEditor;
 
        if (this.worksheetEditor != null)
        {
            this.worksheetEditor.Selection.SelectionChanged += this.Selection_SelectionChanged;
        }
    }
 
    private void Selection_SelectionChanged(object sender, EventArgs e)
    {
        this.InvalidateCanExecute();
    }
}

The next step is to instantiate the SelectionDependentCommand and plug it to the current context menu:

public partial class MainWindow : Window
{
    private const string ContextMenuItemHeader = "Highlight";
    private readonly PatternFill highlightFill;
    private RadWorksheetEditor worksheetEditor;
 
    public MainWindow()
    {
        this.highlightFill = new PatternFill(PatternType.Solid, Colors.Yellow, Colors.Transparent);
        StyleManager.ApplicationTheme = new Windows8Theme();
        InitializeComponent();
 
        WorkbookFormatProvidersManager.RegisterFormatProvider(new XlsxFormatProvider());
 
        this.AddCustomContextMenuItem();
    }
 
    private void AddCustomContextMenuItem()
    {
        DelegateCommand highlightCommand = null;
 
        Action<object> highlightCommandAction = new Action<object>((parameter) =>
        {
            this.radSpreadsheet.ActiveWorksheetEditor.Selection.Cells.SetFill(highlightFill);
            highlightCommand.InvalidateCanExecute();
        });
 
        Predicate<object> highlightCommandPredicate = new Predicate<object>((obj) =>
        {
            RadWorksheetEditor editor = this.radSpreadsheet.ActiveWorksheetEditor;
            if (editor != null)
            {
                PatternFill patternFill = editor.Worksheet.Cells[editor.Selection.ActiveRange.SelectedCellRange].GetFill().Value as PatternFill;
                if (patternFill != null)
                {
                    return !patternFill.Equals(highlightFill);
                }
            }
 
            return false;
        });
 
        highlightCommand = new SelectionDependentCommand(this.radSpreadsheet, highlightCommandAction, highlightCommandPredicate);
 
        RadMenuItem newItem = new RadMenuItem();
        newItem.Header = ContextMenuItemHeader;
        newItem.Command = highlightCommand;
 
        this.radSpreadsheet.WorksheetEditorContextMenu.Items.Add(newItem);
    }
}

Now when you right-click the current selection the context menu contains the copy and paste commands along with the custom Highlight option:

Click the Highlight option and the background of all selected cells will turn yellow. Note that now if your selection includes highlighted cells, the option will not be available in the context menu:

That’s it! Now you know everything you need to create or change RadSpreadsheet’s Context Menu according to your needs. All the code you see here can be found in our SDK Examples. So make sure to check it out and play around with the source.

Stay tuned for more Spreadsheet customization tips and tricks!

Boryana Goncharenko,
Software Developer


Related Posts

Comments