Telerik MVC : Resizing Controls Within Splitter Panes

This website is no longer actively supported

Written by John DeVight on 12 May 2011 15:42
Last Updated by John DeVight on 13 Jun 2011 13:30

Download RAZOR Source Code
Download ASPX Source Code
* Note: the Telerik.Web.Mvc.dll, Telerik "Contents" and Telerik "Scripts" have all been removed to reduce the zip file size

Overview

In the wiki page Telerik MVC : Creating an "ExtJS border-layout" with Splitters I showed how to resize Telerik MVC Splitters when the browser window is resized. However, the content of the Inner Splitter's "content pane" doesn't resize. To accomplish this the View subscribes to the browser window's resize event. However, if the Splitter is resized, the View doesn't know to resize the controls in the "content pane". To solve this, I needed to subscribe to the Inner Splitter's OnResize client event for the "content pane". However, the Splitter controls are on my (_Layout.cshtml or Site.Master) page. I don't want to have the (_Layout.cshtml or Site.Master) page know about every View that could be rendered to resize the controls in the "content pane". I wanted each View to take responsibility for resizing it's controls. To do this, I implemented the publisher / subscriber design pattern as explained on Publisher / Subscriber Design Pattern : A JavaScript Implementation. the _Layout.cshtml page notifies the Message Broker that the "content pane" has been resized, and each View that is interested subscribes to the event and resizes it's controls appropriately.

In the following example, I continued with the code from Telerik MVC : Creating an "ExtJS border-layout" with Splitters and added a Telerik MVC TabStrip to my Index View. When the browser window is resized or the "content pane" is resized, the Index View resizes the TabStrip.

What you will end up with in the end is this:

resizing-controls-within-splitter-panes.jpg

Implementing (_Layout.cshtml or Site.Master) Page

The (_Layout.cshtml or Site.Master) contains the Telerik MVC Splitters as described in Telerik MVC : Creating an "ExtJS border-layout" with Splitters with the addition of the OnResize client event for the "content" pane. Also, the Html.Telerik().ScriptRegistrar() adds the observer.js to the DefaultGroup.

Here is the code:

<body>
    <div id="page">
        <div id="main">
                @{Html.Telerik().Splitter().Name("MainSplitter")
                    .HtmlAttributes(new { style = "height: 300px;" })
                    .Orientation(SplitterOrientation.Vertical)
                    .Panes(vPanes =>
                    {
                        vPanes.Add()
                            .Size("50px")
                            @* Added the following HTML styles to allow the menu to display over top of all other div's.
                             * overflow:visible - needed for Firefox 3.x and IE 8.
                             * position:absolute; z-index:100; - needed for IE 7.
                             *@
                            .HtmlAttributes(new { style = "overflow:visible; position:absolute; z-index:100;" })
                            .Collapsible(false)
                            .Resizable(false)
                            .Scrollable(false)
                            .Content(
                                @<text>
                                    <div class="splitterContent" style="width:100%;"><h1>My Application</h1></div>
                                </text>
                            );

                        vPanes.Add()
                            .Scrollable(false)
                            .Content(Html.Telerik().Splitter().Name("InnerSplitter")
                                .HtmlAttributes(new { style = "height: 100%; width: 100%; border: 0; overflow: hidden;" })
                                .Orientation(SplitterOrientation.Horizontal)
                                .ClientEvents(events => events.OnResize("Layout.InnerSplitter_OnResize"))
                                .Panes(hPanes =>
                                {
                                    hPanes.Add()
                                        .Size("250px")
                                        .Collapsible(true)
                                        .Content(@<h2>Navigation Here</h2>);

                                    hPanes.Add()
                                        .Content(@<div class="splitterContent">@RenderBody()</div>);
                                }).ToString());

                        vPanes.Add()
                            .Size("25px")
                            .Collapsible(false)
                            .Resizable(false)
                            .Scrollable(false)
                            .Content(@<div class="splitterContent"></div>);
                    })
                    .Render();
                }
        </div>

    </div>

    @{
        Html.Telerik().ScriptRegistrar()
            .DefaultGroup(group =>
                group.Add("~/Scripts/observer.js")
            )
            .Scripts(scripts =>
                scripts.AddGroup("LayoutGroup", group =>
                    group.Add("~/Scripts/Shared/Layout.js")
                )
            )
            .OnDocumentReady(
                @<text>
                    Layout.Init();
                </text>
            );
    }

    @Html.Telerik().ScriptRegistrar()
</body>

Implementing Layout.js

The Layout class in Layout.js handles resizing the splitter and also implements an event handler for the OnResize client event for the "content" pane. The event handler is called Layout.InnerSplitter_OnResize and notifies the MessageBroker of an event called "InnerSplitterResizeEvent".

Here is the code:

Layout.InnerSplitter_OnResize = function(e) {
    _messageBroker.Notify("InnerSplitterResizeEvent");
}

Implementing The Index View

The Index View contains a Telerik MVC TabStrip. The Index View also registers the Index.js script and calls Index.Init() when the Index View is rendered.

Here is the code:

@using Telerik.Web.Mvc.UI;
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@(Html.Telerik().TabStrip()
    .Name("IndexTabStrip")
    .Items(tabstrip =>
    {
        tabstrip.Add()
            .Text("Tab One")
            .Selected(true)
            .Content(
                @<text>
                    <div id="tabOneContents" style="overflow: auto;">
                        Tab One Contents Here
                    </div>
                </text>
            );
        tabstrip.Add()
            .Text("Tab Two")
            .Content(
                @<text>
                    <div id="tabTwoContents" style="overflow: auto;">
                        Tab Two Contents Here
                    </div>
                </text>
            );
    })
)

@{
    Html.Telerik().ScriptRegistrar()
        .Scripts(scripts =>
            scripts.AddGroup("IndexGroup", group =>
                group.Add("~/Scripts/Home/Index.js")
            )
        )
        .OnDocumentReady(
            @<text>
                Index.Init();
            </text>
        );
}

Implementing Index.js

The Index class subscribes to the browser window's resize event to resize the TabStrip when the browser window is resized. Additionally, the Index class subscribes with the MessageBroker to receive "InnerSplitterResizeEvent" events.

The Index class resizes the TabStrip and each of the tab contents in the TabStrip. I do this by placing a div as the "parent" element for each Tab's contents. The div implements the style "overflow:auto;" so that scroll bars appear in the tab if the contents exceeds the size of the tab.

Here is the code:

var Index = {};

Index.Init = function() {
    Index.Resize();
    $(window).resize(Index.Resize);

    _messageBroker.AddSubscriber("InnerSplitterResizeEvent", function() { Index.Resize(); });
}

Index.Resize = function() {
    var contentPane =  $($('#InnerSplitter').children()[2]);
    var height = contentPane.height();
    var width = contentPane.width();

    // Resize the tab control to fit in the contract splitter.
    var tabStripCtrl = $('#IndexTabStrip');
    tabStripCtrl.height(height - 2);
    tabStripCtrl.width(width - 2);

    $('#tabOneContents').height(tabStripCtrl.height() - 50);
    $('#tabTwoContents').height(tabStripCtrl.height() - 50);
}

Summary

By implementing this solution, each View can take responsibility for rendering it's controls.

References

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License