[CLOSED] SplitButton overflow menu not shown correctly

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1

    [CLOSED] SplitButton overflow menu not shown correctly

    Hello support team,
    please refer to a considerably simplified test case of relatively sophisticated application toolbar rendering (based on button groups / buttons registration and user preferences). When using SplitButton in the toolbar, I noticed the strange behavior of the overflow menu.

    _LayoutPartial.cshtml:
    @{
        Layout = null;
        var X = Html.X();
    }
    
    @(Html.X().ResourceManager())
    
    @{Container container = X.Container().ItemsFromPartial("ApplicationToolbar");}
    
    @(
        X.Window().Width(400).Height(200).Items(
            X.Panel()
            .Layout(LayoutType.Fit)
            .BodyPadding(0)
            .Border(false)
            .DockedItems(container)
            .ItemsFromSection(this, "ITEMS", true)
        )
    )
    
    @RenderBody()
    ApplicationToolbar.cshtml:
    @using TestCases.Models;
    
    @{AppToolbarRenderer renderer = new AppToolbarRenderer();}
    
    @renderer.RenderApplicationToolbar()
    Index.cshtml:
    @{
        Layout = "~/Views/Shared/_LayoutPartial.cshtml";
        var X = Html.X();
    }
    
    @section ITEMS {}
    ToolbarModel.cs:
    using Ext.Net;
    
    namespace TestCases.Models
    {
        public class AppToolbarRenderer
        {
            public Toolbar.Builder RenderApplicationToolbar()
            {
                Toolbar toolbar = new Toolbar()
                {
                    Border = false,
                    Dock = Dock.Top,
                    EnableOverflow = true,
                    OverflowHandler = OverflowHandler.Menu
                };
    
                toolbar.Plugins.Add(new BoxReorderer());
                toolbar.LayoutConfig.Add(new HBoxLayoutConfig() { Align = HBoxAlign.Stretch });
    
                ButtonGroup grpSave = new ButtonGroup { Title = "Save" };
    
                // just to make toolbar wider:            
                grpSave.Items.Add(new Button { Icon = Icon.Disk, IconAlign = IconAlign.Top, Text = "Save" });
                grpSave.Items.Add(new Button { Icon = Icon.DiskEdit, IconAlign = IconAlign.Top, Text = "Save+New" });
                grpSave.Items.Add(new Button { Icon = Icon.DiskMagnify, IconAlign = IconAlign.Top, Text = "Save+Clone" });
                grpSave.Items.Add(new Button { Icon = Icon.DiskError, IconAlign = IconAlign.Top, Text = "Save+Close" });
    
                ButtonGroup grpOutput = new ButtonGroup { Title = "Output", Columns = 2 };
    
                SplitButton btn1 = AppToolbarSplitButton.Print();
                btn1.Menu[0].Add(new Ext.Net.MenuItem("Print Option 1"));
                btn1.Menu[0].Add(new Ext.Net.MenuItem("Print Option 2"));
    
                SplitButton btn2 = AppToolbarSplitButton.Preview();
                btn2.Menu[0].Add(new Ext.Net.MenuItem("Preview Option 1"));
                btn2.Menu[0].Add(new Ext.Net.MenuItem("Preview Option 2"));
    
                SplitButton btn3 = AppToolbarSplitButton.Pdf();
                btn3.Menu[0].Add(new Ext.Net.MenuItem("Pdf Option 1"));
                btn3.Menu[0].Add(new Ext.Net.MenuItem("Pdf Option 2"));
    
                grpOutput.Items.Add(btn1);
                grpOutput.Items.Add(btn2);
                grpOutput.Items.Add(btn3);
    
                toolbar.Items.Add(grpSave);
                toolbar.Items.Add(grpOutput);
    
                return toolbar.ToBuilder();
            }
        }
    
        public class AppToolbar : Toolbar { }
    
        public class AppToolbarSplitButton : SplitButton
        {
            public AppToolbarSplitButton()
            {
                Scale = ButtonScale.Small;
                IconAlign = IconAlign.Top;
            }
    
            public static AppToolbarSplitButton Print()
            {
                AppToolbarSplitButton btn = new AppToolbarSplitButton
                {
                    Icon = Icon.Printer,
                    Text = "Print",
                    OverflowText = "Print",
                    ArrowVisible = false,
                    RowSpan = 2
                };
    
                Menu m = new Menu();
                m.Listeners.Add.Handler = "if (this.items.length == 1) { this.ownerCmp.setArrowVisible(true); if (this.ownerCmp.ownerCt && !this.ownerCmp.ownerCt.hidden) this.ownerCmp.ownerCt.updateLayout(); }"; // show arrow after the first menu item is added
    
                btn.Menu.Add(m);
    
                return btn;
            }
    
            public static AppToolbarSplitButton Preview()
            {
                AppToolbarSplitButton btn = new AppToolbarSplitButton
                {
                    Icon = Icon.Layout,
                    IconAlign = IconAlign.Left,
                    Text = "Preview",
                    TextAlign = ButtonTextAlign.Left,
                    OverflowText = "Preview",
                    ArrowVisible = false,
                    WidthSpec = "100%"
                };
    
                Menu m = new Menu();
                m.Listeners.Add.Handler = "if (this.items.length == 1) { this.ownerCmp.setArrowVisible(true); if (this.ownerCmp.ownerCt && !this.ownerCmp.ownerCt.hidden) this.ownerCmp.ownerCt.updateLayout() }";
                btn.Menu.Add(m);
    
                return btn;
            }
    
            public static AppToolbarSplitButton Pdf()
            {
                AppToolbarSplitButton btn = new AppToolbarSplitButton
                {
                    Icon = Icon.PageWhiteAcrobat,
                    IconAlign = IconAlign.Left,
                    Text = "PDF",
                    TextAlign = ButtonTextAlign.Left,
                    OverflowText = "PDF",
                    ArrowVisible = false,
                    WidthSpec = "100%"
                };
    
                Menu m = new Menu();
                m.Listeners.Add.Handler = "if (this.items.length == 1) { this.ownerCmp.setArrowVisible(true); if (this.ownerCmp.ownerCt && !this.ownerCmp.ownerCt.hidden) this.ownerCmp.ownerCt.updateLayout() }";
    
                btn.Menu.Add(m);
    
                return btn;
            }
        }
    }
    There are several issues accountered here:
    1. The overflow menu is displayed incorrectly (it takes the full width of the browser)
    2. After toolbar (window) resize, the menu on split buttons is not shown
    3. After toolbar (window) resize, the "Output" button group and the "Overflow" menu button are rendered "randomly" - shown/hidden independently on the available space

    Am I doing something wrong?

    Ext.NET 4.7.1
    Ext JS 6.6.0.258

    Thank you for your assistance.

    Dan
    Last edited by fabricio.murta; Oct 10, 2018 at 3:51 PM.
  2. #2
    Hello, @NewLink!

    Thanks for trying to simplify the test case, we have tried to run it but it seems, after adding all code, at least the controllers code is missing. Have you tried to reduce this issue down to single-page, all rendered from razor code? It would be interesting to know if the page issues are introduced because of partial views, or due to the way you build the components (via model calls).

    Maybe this can still be simplified down to a one-page, one-file WebForms page, with just the bare minimum to reproduce the issue? Or if you try it, you get the correct output?

    Here something I've tried, but can't say I'm really reproducing the page you want to draw:

    <%@ Page Language="C#" %>
    
    <!DOCTYPE html>
    
    <script runat="server">
        public void Page_Load(object sender, EventArgs e)
        {
            // just in case some code behind algorithm is necessary.
        }
    
    </script>
    <html>
    <head runat="server">
        <title>Ext.NET Example</title>
    
    </head>
    <body>
        <form id="Form1" runat="server">
            <ext:ResourceManager runat="server" />
            <ext:Window runat="server" ID="wnd1" Width="400" Height="200">
                <Items>
                    <ext:Panel runat="server" ID="pnl1" BodyPadding="0" Border="false">
                        <DockedItems>
                            <ext:Toolbar runat="server" ID="tlb1" Dock="Top" EnableOverflow="true" OverflowHandler="Menu">
                                <LayoutConfig><ext:HBoxLayoutConfig Align="Stretch" /></LayoutConfig>
                                <Plugins>
                                    <ext:BoxReorderer runat="server" ID="brp1" />
                                </Plugins>
                                <Items>
                                    <ext:Button runat="server" ID="btn1" Icon="Disk" Text="Save" IconAlign="Top" />
                                    <ext:Button runat="server" ID="btn2" Icon="DiskEdit" Text="Save+New" IconAlign="Top" />
                                    <ext:Button runat="server" ID="btn3" Icon="DiskMagnify" Text="Save+Clone" IconAlign="Top" />
                                    <ext:Button runat="server" ID="btn4" Icon="DiskError" Text="Save+Close" IconAlign="Top" />
                                    <ext:ButtonGroup runat="server" ID="btg1" Title="Output" Columns="2">
                                        <Items>
                                            <ext:SplitButton runat="server" ID="sbt1" Icon="Printer" Text="Print" RowSpan="2">
                                                <Menu>
                                                    <ext:Menu runat="server" ID="mnu1">
                                                        <Items>
                                                            <ext:MenuItem runat="server" Text="Print Option 1" />
                                                            <ext:MenuItem runat="server" Text="Print Option 2" />
                                                        </Items>
                                                    </ext:Menu>
                                                </Menu>
                                            </ext:SplitButton>
                                            <ext:SplitButton runat="server" ID="sbt2" Icon="Layout" Text="Preview" RowSpan="2">
                                                <Menu>
                                                    <ext:Menu runat="server" ID="mnu2">
                                                        <Items>
                                                            <ext:MenuItem runat="server" Text="Preview Option 1" />
                                                            <ext:MenuItem runat="server" Text="Preview Option 2" />
                                                        </Items>
                                                    </ext:Menu>
                                                </Menu>
                                            </ext:SplitButton>
                                            <ext:SplitButton runat="server" ID="sbt3" Icon="Printer" Text="PDF" RowSpan="2">
                                                <Menu>
                                                    <ext:Menu runat="server" ID="mnu3">
                                                        <Items>
                                                            <ext:MenuItem runat="server" Text="PDF Option 1" />
                                                            <ext:MenuItem runat="server" Text="PDF Option 2" />
                                                        </Items>
                                                    </ext:Menu>
                                                </Menu>
                                            </ext:SplitButton>
                                        </Items>
                                    </ext:ButtonGroup>
                                </Items>
                            </ext:Toolbar>
                        </DockedItems>
                    </ext:Panel>
                </Items>
            </ext:Window>
        </form>
    </body>
    </html>
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Hi FabrÃ*cio,
    thank you for answer. I understand your reproach very well and you are right... it was not simplified enough.

    I did not mention the controller because it is very simple:
    using System.Web.Mvc;
    
    namespace TestCases.Controllers
    {
        public class ToolbarController : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
        }
    }
    In my view, the way the toolbar is built has a significant effect on the result. I will try to simplify the test case even more so that I get the complained output with a minimum code. I will reply later.

    Thank you for assistance.

    Dan
    Last edited by NewLink; Sep 12, 2018 at 11:11 PM.
  4. #4
    Hi FabrÃ*cio,
    a simplified test case is here:
    @{
        Layout = null;
        var X = Html.X();
    
        AbstractComponent[] toolbar = new AbstractComponent[]
        {
            X.Toolbar().Border(false).Dock(Dock.Top).EnableOverflow(true).OverflowHandler(OverflowHandler.Menu).LayoutConfig(new HBoxLayoutConfig() { Align = HBoxAlign.Stretch }).Items(
                X.ButtonGroup().Title("Output").Columns(2).Items(
                    X.SplitButton().Text("Print").OverflowText("Print").Icon(Icon.Printer).RowSpan(2).Scale(ButtonScale.Large).Menu(
                        X.Menu().Items(
                            X.MenuItem().Text("Print Option 1"),
                            X.MenuItem().Text("Print Option 2")
                        )
                    ),
                    X.SplitButton().Text("Preview").OverflowText("Preview").Icon(Icon.Layout).IconAlign(IconAlign.Left).TextAlign(ButtonTextAlign.Left).WidthSpec("100%").Menu(
                        X.Menu().Items(
                            X.MenuItem().Text("Preview Option 1"),
                            X.MenuItem().Text("Preview Option 2")
                        )
                    ),
                    X.SplitButton().Text("PDF").OverflowText("PDF").Icon(Icon.PageWhiteAcrobat).IconAlign(IconAlign.Left).TextAlign(ButtonTextAlign.Left).WidthSpec("100%").Menu(
                        X.Menu().Items(
                            X.MenuItem().Text("Pdf Option 1"),
                            X.MenuItem().Text("Pdf Option 2")
                        )
                    )
                )
            )
        };
    }
    
    @(Html.X().ResourceManager())
    
    @(
        X.Window().Width(100).Height(150).Items(
            X.Panel()
            .Layout(LayoutType.Fit)
            .BodyPadding(0)
            .Border(false)
            .DockedItems(toolbar)
        )
    )
    I apologize for my laziness to provide a meaningful test case right from the start.

    Dan
  5. #5
    Hello @NewLink!

    Alright, now it is very clear what's up! Thank you for taking your time to simplify the issue!

    The issue is with the WidthSpec="100%" in your code. It is making the menu entry occupy all the width when displayed in the overflow menu.

    I believe you want something similar to MS-Office look & feel, right? Aligned buttons, and the group collapses in its entirety when it does not fit the window width, leaving other button groups if they still fit there (left-aligned).

    If my guess is correct, the default column layout assigned to button groups is not ideal, and you can just wrap button groups as containers in horizontal-vertical wrapping to have the same column behavior, yet ensuring better dynamic readjustment as the window is resized.

    Here's a suggestion to fix your scenario.

    In summary:

    - uses vbox/hbox layout on button groups instead of the column layout
    - drops .WidthSpec("100%") from buttons in favor of the .Flex(1)
    - arranges vertically-aligned buttons within inner vbox-aligned button groups (vertical button groups within horizontal button groups) to mimic the column behavior and dynamically fit components to the available width
    - guarantees minimum button group width (could use also MaxWidth to determine its maximum, or just give fixed widths so that buttons therein are always the same width)
    - addresses an issue where buttons are hidden to the overflow menu, expanded, re-displayed in the toolbar, and expanded (menu was not then showing up anymore)

    I hope this is a suitable approach for your scenario.

    @{
        Layout = null;
        var X = Html.X();
    }
    
    @(Html.X().ResourceManager())
    
    @(
        // Resizable window
        X.Window().Width(100).Height(180).DockedItems(
            // Toolbar, stretching its entries horizontally to fit its width.
            X.Toolbar()
                .Border(false)
                .Dock(Dock.Top)
                .EnableOverflow(true) // This already implies .OverflowHandler(OverflowHandler.Menu)
                .LayoutConfig(new HBoxLayoutConfig() { Align = HBoxAlign.Stretch })
                .Items(
                    // Outer button group, ensuring everything on it will be hidden whenever any part
                    // of it does not fit the toolbar's width.
                    X.ButtonGroup()
                        .Title("Output")
                        .Flex(1)
                        .MinWidth(300)
                        .LayoutConfig(new HBoxLayoutConfig() { Align = HBoxAlign.Stretch })
                        .Items(
                            X.SplitButton()
                                .Text("Print")
                                .OverflowText("Print")
                                .Icon(Icon.Printer)
                                .Scale(ButtonScale.Large)
                                .Flex(1)
                                .Menu(
                                    X.Menu().Items(
                                        X.MenuItem().Text("Print Option 1"),
                                        X.MenuItem().Text("Print Option 2")
                                    )
                                )
                                // This listener fixes an issue that prevents the menu from showing
                                // when the button is once expanded via the overflow menu and then
                                // displayed again. Should be present in all instances of the split
                                // buttons.
                                .Listeners(l => l.MenuShow.Handler = Ext.Net.MvcUtils.StringifyScriptBlock(@<text>
                                    <script>
                                        if (this.menu.ownerCmp !== this) {
                                            this.menu.setOwnerCmp(this);
                                        }
                                    </script>
                                </text>)),
                                // A vertical-filling button group for the buttons spanning one row each.
                                X.ButtonGroup()
                                    .Flex(1)
                                    .LayoutConfig(new VBoxLayoutConfig() { Align = VBoxAlign.Stretch })
                                    .Items(
                                        X.SplitButton()
                                            .Text("Preview")
                                            .OverflowText("Preview")
                                            .Icon(Icon.Layout)
                                            .IconAlign(IconAlign.Left)
                                            .TextAlign(ButtonTextAlign.Left)
                                            .Flex(1)
                                            .Menu(
                                                X.Menu().Items(
                                                    X.MenuItem().Text("Preview Option 1"),
                                                    X.MenuItem().Text("Preview Option 2")
                                                )
                                            )
                                            .Listeners(l => l.MenuShow.Handler = Ext.Net.MvcUtils.StringifyScriptBlock(@<text>
                                                <script>
                                                    if (this.menu.ownerCmp !== this) {
                                                        this.menu.setOwnerCmp(this);
                                                    }
                                                </script>
                                            </text>)),
                                        X.SplitButton()
                                            .Text("PDF")
                                            .OverflowText("PDF")
                                            .Icon(Icon.Layout)
                                            .IconAlign(IconAlign.Left)
                                            .TextAlign(ButtonTextAlign.Left)
                                            .Flex(1)
                                            .Menu(
                                                X.Menu().Items(
                                                    X.MenuItem().Text("Pdf Option 1"),
                                                    X.MenuItem().Text("Pdf Option 2")
                                                )
                                            )
                                            .Listeners(l => l.MenuShow.Handler = Ext.Net.MvcUtils.StringifyScriptBlock(@<text>
                                                <script>
                                                    if (this.menu.ownerCmp !== this) {
                                                        this.menu.setOwnerCmp(this);
                                                    }
                                                </script>
                                            </text>))
                                    )
                        )
                )
        )
    )
    And hope this helps!
    Fabrício Murta
    Developer & Support Expert
  6. #6
    Hi FabrÃ*cio,
    thank you for your suggestion. You're right, my intention is to create a toolbar similar to MS Office. Your scenario works well and is almost perfect, but dynamic toolbar readjustment to fill the entire width of the window is not - in my opinion - the best solution. If the toolbar has many button groups, including ToolbarSpacer and ToolbarFill place-holders, the result looks weird.

    However, the introduction of the MenuShow listener has a great value for me because it solves one of the issues I had with the split buttons.

    I slightly modified your code and what I want to achieve is something like this:
    @{
        Layout = null;
        var X = Html.X();
    }
    
    @(Html.X().ResourceManager())
    
    @(
        // Resizable window
        X.Window().Width(400).Height(180).DockedItems(
            // Toolbar, stretching its entries horizontally to fit its width.
            X.Toolbar()
                .Border(false)
                .Dock(Dock.Top)
                .EnableOverflow(true) // This already implies .OverflowHandler(OverflowHandler.Menu)
                .LayoutConfig(new HBoxLayoutConfig() { Align = HBoxAlign.Stretch })
                .Items(
                    // Outer button group, ensuring everything on it will be hidden whenever any part
                    // of it does not fit the toolbar's width.
                    X.ButtonGroup()
                        .Title("Output")
                        //.Flex(1)
                        //.MinWidth(300)
                        .LayoutConfig(new HBoxLayoutConfig() { Align = HBoxAlign.Stretch })
                        .Items(
                            X.SplitButton()
                            .Text("Print")
                            .OverflowText("Print")
                            .IconUrl("http://individual.icons-land.com/IconsPreview/BaseSoftware/PNG/32x32/Printer.png")
                            .Scale(ButtonScale.Large)
                            .Flex(1)
                            .Menu(
                                X.Menu().Items(
                                    X.MenuItem().Text("Print Option 1"),
                                    X.MenuItem().Text("Print Option 2")
                                )
                            )
                            // This listener fixes an issue that prevents the menu from showing
                            // when the button is once expanded via the overflow menu and then
                            // displayed again. Should be present in all instances of the split
                            // buttons.
                            .Listeners(l => l.MenuShow.Handler = Ext.Net.MvcUtils.StringifyScriptBlock(
                                @<text>
                                    <script>
                                        if (this.menu.ownerCmp !== this) {
                                            this.menu.setOwnerCmp(this);
                                        }
                                    </script>
                                </text>)
                            ),
                            X.ButtonGroup()
                                .Flex(1)
                                .LayoutConfig(new VBoxLayoutConfig() { Align = VBoxAlign.Stretch })
                                .Items(
                                    X.SplitButton()
                                        .Text("Preview")
                                        .OverflowText("Preview")
                                        .Icon(Icon.Layout)
                                        .IconAlign(IconAlign.Left)
                                        .TextAlign(ButtonTextAlign.Left)
                                        .Flex(1)
                                        .Menu(
                                            X.Menu().Items(
                                                X.MenuItem().Text("Preview Option 1"),
                                                X.MenuItem().Text("Preview Option 2")
                                            )
                                        )
                                        .Listeners(l => l.MenuShow.Handler = Ext.Net.MvcUtils.StringifyScriptBlock(
                                            @<text>
                                                <script>
                                                    if (this.menu.ownerCmp !== this) {
                                                    this.menu.setOwnerCmp(this);
                                                    }
                                                </script>
                                            </text>)
                                        ),
                                    X.SplitButton()
                                        .Text("PDF")
                                        .OverflowText("PDF")
                                        .Icon(Icon.PageWhiteAcrobat)
                                        .IconAlign(IconAlign.Left)
                                        .TextAlign(ButtonTextAlign.Left)
                                        .Flex(1)
                                        .Menu(
                                            X.Menu().Items(
                                                X.MenuItem().Text("Pdf Option 1"),
                                                X.MenuItem().Text("Pdf Option 2")
                                            )
                                        )
                                        .Listeners(l => l.MenuShow.Handler = Ext.Net.MvcUtils.StringifyScriptBlock(
                                            @<text>
                                                <script>
                                                    if (this.menu.ownerCmp !== this) {
                                                        this.menu.setOwnerCmp(this);
                                                    }
                                                </script>
                                            </text>)
                                        )
                                )
                        ),
                    X.ButtonGroup()
                        .Title("Ext.NET")
                        //.Flex(1)
                        //.MinWidth(300)
                        .LayoutConfig(new HBoxLayoutConfig() { Align = HBoxAlign.Stretch })
                        .Items(
                            X.Button()
                            .Text("Home")
                            .OverflowText("Home")
                            .IconUrl("http://speed.ext.net/identity/extnet-icon-32x32.png")
                            .Scale(ButtonScale.Large)
                            .Flex(1),
                            X.ButtonGroup()
                                .Flex(1)
                                .LayoutConfig(new VBoxLayoutConfig() { Align = VBoxAlign.Stretch })
                                .Items(
                                    X.Button()
                                        .Text("Documentation")
                                        .OverflowText("Documentation")
                                        .Icon(Icon.Page)
                                        .IconAlign(IconAlign.Left)
                                        .TextAlign(ButtonTextAlign.Left)
                                        .Flex(1),
                                    X.Button()
                                        .Text("Examples")
                                        .OverflowText("Examples")
                                        .Icon(Icon.Cog)
                                        .IconAlign(IconAlign.Left)
                                        .TextAlign(ButtonTextAlign.Left)
                                        .Flex(1)
                                )
                        )
                )
        )
    )
    All groups and buttons are now perfectly aligned, but I have two more problems:
    1. When downsized the window an error occures Cannot read property 'setProp' of null.
    2. I'm not able to get rid of outline of the inner button groups.

    It is worth to mention that our framework is multilingual, so working with fixed button/button group width is not possible (because we cannot predict translation string length).

    Is this approach completely wrong or is there any better solution to this scenario?

    Thank you for help.

    Dan

Similar Threads

  1. [CLOSED] change Toolbar overflow menu icons
    By RCM in forum 2.x Legacy Premium Help
    Replies: 4
    Last Post: Jan 16, 2015, 2:28 PM
  2. [CLOSED] Update SplitButton menu on DirectEvent?
    By rthiney in forum 2.x Legacy Premium Help
    Replies: 1
    Last Post: Jun 07, 2013, 4:10 AM
  3. [CLOSED] Updating the Overflow Menu
    By musher in forum 1.x Legacy Premium Help
    Replies: 5
    Last Post: Apr 25, 2012, 2:40 PM
  4. [CLOSED] Splitbutton with Menu - Make the menu expand upwards?
    By rbarr in forum 1.x Legacy Premium Help
    Replies: 2
    Last Post: Aug 02, 2011, 1:18 PM
  5. [CLOSED] Images in SplitButton Menu off
    By rthiney in forum 1.x Legacy Premium Help
    Replies: 1
    Last Post: Mar 01, 2010, 5:50 PM

Posting Permissions