This is a migrated thread and some comments may be shown as answers.

Split Button UserControl - integration with RadGrid OnItemCommand

1 Answer 80 Views
Button
This is a migrated thread and some comments may be shown as answers.
Thad
Top achievements
Rank 2
Thad asked on 01 Apr 2013, 05:05 PM
Hello community!

I've wrapped a RadButton and RadContextMenu into a server user control and am trying to find out if it is possible to integrate the usercontrol with a RadGrid so that when a RadMenuItem is clicked it can fire the RadGrid_OnItemCommand event just like clicking on a RadButton would do...

My goal is to catch the server side click event of the RadMenuItem and invoke the click event of the RadButton, thereby fooling the RadGrid into using the CommandName and CommandArgument passed along in a CommandEventArgs object.

This is how the control is added to the HTML markup:
<telerik:GridTemplateColumn HeaderText="Actions" HeaderStyle-Width="125px" ItemStyle-Width="125px">
    <ItemTemplate>
        <fmh:ActionButton runat="server" CommandArgument='<%# Eval("FieldId") %>' ID="abGridTest"
            OnMenuItemClicked="abGridTest_OnMenuItemClicked" >
            <ActionItems>
                <fmh:ActionItem Text="Example 1" Icon="save" CommandName="test1" />
                <fmh:ActionItem IsSeparator="true" />
                <fmh:ActionItem Text="Example 2" Icon="edit" CommandName="test2" />
            </ActionItems>
        </fmh:ActionButton>
    </ItemTemplate>
</telerik:GridTemplateColumn>

Here is a stripped down version of my user control that just does the basics:
public class ActionItem
{
    public ActionItem()
    {
        Visible = true;
    }
    public bool IsSeparator { get; set; }
    public bool Visible { get; set; }
    public string CommandName { get; set; }
    public string Icon { get; set; }
    public string Text { get; set; }
}
 
[ParseChildren(true)]
public sealed class ActionButton : DataBoundControl, INamingContainer
{
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(ActionButton))]
    public ITemplate ContentTemplate { get; set; }
     
    [Bindable(true), DefaultValue("")]
    public string CommandArgument
    {
        get
        {
            object o = ViewState[ClientID + "_CommandArgument"];
            return o == null ? "0" : o.ToString();
        }
        set { ViewState[ClientID + "_CommandArgument"] = value; }
    }
 
    [MergableProperty(false), PersistenceMode(PersistenceMode.InnerProperty), DefaultValue((string)null)]
    public Collection<ActionItem> ActionItems { get; set; }
 
    public event CommandEventHandler MenuItemClicked;
    public RadContextMenu ContextMenu { get; set; }
    public RadButton Button { get; set; }
 
    protected override void CreateChildControls()
    {
        base.CreateChildControls();
 
        if (ContentTemplate != null)
            ContentTemplate.InstantiateIn(this);
 
        SetupButtonAndContextMenu();
        if (ActionItems != null)
        {
            foreach (ActionItem actionItem in ActionItems)
            {
                RadMenuItem rmi = new RadMenuItem();
 
                SetupMenuItem(actionItem, rmi);
 
                rmi.PostBack = true;
                rmi.Visible = actionItem.Visible;
 
                ContextMenu.Items.Add(rmi);
            }
        }
 
        Controls.Add(Button);
        Controls.Add(ContextMenu);
    }
 
    private void ContextMenuOnItemClick(object sender, RadMenuEventArgs radMenuEventArgs)
    {
        CommandEventArgs commandEventArgs = new CommandEventArgs(radMenuEventArgs.Item.Attributes["CommandName"], CommandArgument);
 
        // This is what I *thought* I would need to do, but this isn't working. Wrong binding flags?
        //typeof (RadButton).InvokeMember("Click", BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, Button,
        //new object[] {commandEventArgs});

       
// This works just fine...because I have a reference to the event already
        
MenuItemClicked.Invoke(this,
            new CommandEventArgs(radMenuEventArgs.Item.Attributes["CommandName"], CommandArgument));
    }
 
    private void SetupButtonAndContextMenu()
    {
        Button = new RadButton
        {
            ID = "Button",
            CssClass = "Secondary Utility",
            EnableSplitButton = true,
            Text = @"Action",
            AutoPostBack = false,
            OnClientClicked = "$F.ActionButton.onClientClicked",
            EnableEmbeddedSkins = false,
            Skin = "OurSkin",
            Width = Width
        };
 
        Button.Icon.SecondaryIconUrl = "/Common/Images/ButtonIcons/DropDownArrow.png";
 
        ContextMenu = new RadContextMenu
        {
            ID = "Button_Menu",
            ExpandDelay = 0,
            CollapseDelay = 0,
            EnableEmbeddedSkins = false,
            Skin = "OurSkin",
            OnClientItemClicked = "$F.ActionButton.onClientMenuItemClicked"
        };
        ContextMenu.ItemClick += ContextMenuOnItemClick;
 
    }
    private static void SetupMenuItem(ActionItem actionItem, RadMenuItem rmi)
    {
        if (actionItem.IsSeparator)
        {
            rmi.IsSeparator = true;
            return;
        }
 
        if (String.IsNullOrEmpty(actionItem.Text))
        {
            throw new ArgumentNullException("Text", @"ActionItem.Text is a required argument.");
        }
        rmi.Text = actionItem.Text;
        if (!String.IsNullOrEmpty(actionItem.Icon))
            rmi.ImageUrl = "/Common/Images/LinkButtonIcons/" + actionItem.Icon + ".png";
 
        rmi.Attributes["CommandName"] = !String.IsNullOrEmpty(actionItem.CommandName)
                                            ? actionItem.CommandName
                                            : actionItem.Text.RemoveSpaces();
    }
}

Hopefully someone will have an insight for me, or if nothing else you can use what I have so far to some benefit...

Thanks!
Thad

PS:  Here is the JavaScript.  I didn't clean it up so it has a bit of functionality you don't notice above.
$F.ActionButton = {
 
    onClientClicked: function (sender, eventArgs) {
        // Find the context menu and remove animations
        var contextMenu = window.$find(sender.get_id() + '_Menu');
 
        // Remove the animation so that it shows/hides immediately
        contextMenu._slide._collapseAnimation.set_type(window.Telerik.Web.UI.AnimationType.None);
        contextMenu._slide._expandAnimation.set_type(window.Telerik.Web.UI.AnimationType.None);
 
        // If it is already visible, just hide it.  For some reason the contextMenu.get_visible() returns false, hence the dom check.
        if (contextMenu.get_contextMenuElement().style.display == 'block') {
            contextMenu.hide();
            return;
        }
 
        // get the position of the Button that was clicked
        var currentLocation = window.$telerik.getBounds(sender.get_element());
         
        // Calculate position to show the context menu
        var contextMenuLeft = currentLocation.x;
        if (contextMenu.get_attributes().getAttribute('RTL') == 't') {
            var contextMenuWidth = $('#' + contextMenu.get_element().id + '_detached').width();
            if (contextMenuWidth == 0) {
                // reposition off screen so we can calculate the width.  Can't do that until it has been rendered once.
                contextMenu.showAt(-1000, -1000);
                contextMenuWidth = $('#' + contextMenu.get_element().id + '_detached').width();
            }
            contextMenuLeft = currentLocation.x + currentLocation.width - contextMenuWidth;
        }
        var contextMenuTop = currentLocation.y + currentLocation.height;
 
        // Show the context menu
        contextMenu.showAt(contextMenuLeft, contextMenuTop);
    },
 
    onClientMenuItemClicked: function (sender, eventArgs) {
        // Is there a function defined?
        var clientFunction = eventArgs.get_item().get_attributes().getAttribute("ClientFunction");
        if (!clientFunction || clientFunction.trim() == '') return;
 
        // Is this a real function?
        var fn = eval(clientFunction);
        if (!fn) return;
 
        // Build arguments to pass including command name and command argument
        var args = new Object;
        args.commandName = eventArgs.get_item().get_attributes().getAttribute("CommandName");
        args.commandArgument = eventArgs.get_item().get_attributes().getAttribute("CommandArgument");
 
        // Fire off the function passing arguments
        fn(sender, args);
    }

1 Answer, 1 is accepted

Sort by
0
Danail Vasilev
Telerik team
answered on 04 Apr 2013, 02:12 PM
Hi Thad,

Note that you cannot trigger a button click as well as fire the ItemCommand on the server. You can, however, click the button on the client-side, which in turn will fire the ItemCommand. You can also access the target value from the ItemClick of the RadMenu. The Grid - AJAX-enabled Context Menu online demo will spread more light on how to do that.

If the above demo, does not help, could you please give more information about the values you want to access from the ItemCommand, as well as provide more details why do you need to fire the ItemCommand event?

Regards,
Danail Vasilev
the Telerik team
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now.
Tags
Button
Asked by
Thad
Top achievements
Rank 2
Answers by
Danail Vasilev
Telerik team
Share this question
or