Mittwoch, 19. Juni 2013

This Blog moved to http://webapps-in-action.com/

Montag, 17. Juni 2013

Xing User Group

This Blog moved to http://webapps-in-action.com/

I've just created a User Group for anyone on Xing.
http://www.xing.com/net/extnet

Donnerstag, 23. August 2012

*It's working now* JavaScript Intellisense with Telerik in ASP.NET Master Page Project with Visual Studio 2012

This Blog moved to http://webapps-in-action.com/

Microsoft did some great improvments on Visual Studio 2012. With some workarounds it was possible to have Javascript Autocompletion in Pages, UserControls, etc. Now the Reference Directives (http://msdn.microsoft.com/en-us/library/bb385682.aspx) are just working.

But if you are on a Telerik Project it's a bit complicated, offical from Telerik: "RadScriptManager currently does not display Intellisense information for the property and the ScriptReferences." (http://www.telerik.com/help/aspnet-ajax/scriptmanager.html)

You need again a little trick, if you want to use the RadScriptManger instead of the ASP ScriptManager on the MasterPage (there can be only one Manager either Telerik or ASP Standard).

Telerik Client-Side JavaScript Auto-completion / Intellisense in VS2012
jQuery from Assembly is also working ;-)

Go to "Options and Settings" in Visual Studio an change the Settings like this:


Next step is to create the referenced ASPX File with ScriptManager supported by Visual Studio

JSReferences.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="JSReferences.aspx.cs" Inherits="RadControlsWebApp.JSReferences" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Assembly="Telerik.Web.UI, Culture=neutral, PublicKeyToken=121fae78165ba3d4" Name="Telerik.Web.UI.Common.Core.js" />
                <asp:ScriptReference Assembly="Telerik.Web.UI, Culture=neutral, PublicKeyToken=121fae78165ba3d4" Name="Telerik.Web.UI.Common.jQuery.js" />
                <asp:ScriptReference Assembly="Telerik.Web.UI, Culture=neutral, PublicKeyToken=121fae78165ba3d4" Name="Telerik.Web.UI.Common.jQueryInclude.js" />
            </Scripts>
        </asp:ScriptManager>
    </form>
</body>
</html>
Now it's time for proper a Telerik Master Page. Site.Master
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="RadControlsWebApp.Site" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <asp:ContentPlaceHolder ID="head" runat="server">
    </asp:ContentPlaceHolder>
    <telerik:RadStyleSheetManager ID="RadStyleSheetManager1" runat="server" />
</head>
<body>
    <form id="form2" runat="server">
        <telerik:RadScriptManager ID="RadScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.Core.js" />
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.jQuery.js" />
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.jQueryInclude.js" />
                <asp:ScriptReference Path="~/js/jquery-ui-1.8.23.custom.min.js" />
            </Scripts>
        </telerik:RadScriptManager>
        <script type="text/javascript">
            //Put your JavaScript code here.
        </script>
        <telerik:RadAjaxManager ID="RadAjaxManager1" runat="server">
        </telerik:RadAjaxManager>
        <div>
            <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
            </asp:ContentPlaceHolder>
        </div>
    </form>
</body>
</html>
WebForm.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="WebForm.aspx.cs" Inherits="RadControlsWebApp.WebForm" %>

<%@ Register Src="~/WebUserControl.ascx" TagPrefix="uc1" TagName="WebUserControl" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <%--// A SIMPLE BUTTON --%>
    FORM BUTTON:
    <telerik:RadButton runat="server" ID="radButton"></telerik:RadButton>
    <telerik:RadTextBox runat="server" ID="radText"></telerik:RadTextBox>
    <%--// JS SCRIPT --%>
    <telerik:RadScriptBlock runat="server">
        <script type="text/javascript">

            // TELERIK DEFAULT EVENT like jQuery $ready()
            function pageLoad() {
                // GIVE BUTTON A TEXT
                $telerik.findButton("<%=radButton.ClientID%>").set_text("Click me");

                // FOR TESTING MIX SOME TELERIK WITH JQUERY
                $("#" + $telerik.findTextBox("<%=radText.ClientID%>").get_id()).click(function () { alert('jQuery ClickHandler on RadTextBox') });

                // FIRE USER CONTROL LOGIC
                pageLoadUserControl();
            }
        </script>
    </telerik:RadScriptBlock>
    <%--// USER CONTROL--%>
    <uc1:WebUserControl runat="server" ID="WebUserControl" />
</asp:Content>
WebUserControl.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl.ascx.cs" Inherits="RadControlsWebApp.WebUserControl" %>

<%--// ANOTHER SIMPLE BUTTON IN USER CONTROL --%>
<br />
USER CONTROL BUTTON:
<telerik:RadButton runat="server" ID="radButton"></telerik:RadButton>
<%--// MORE JS SCRIPT --%>
<telerik:RadScriptBlock ID="RadScriptBlock2" runat="server">
    <script type="text/javascript">
        function pageLoadUserControl() {
            $telerik.findButton("<%=radButton.ClientID%>").set_text("Click me");
            $.ajax(
        }
    </script>
</telerik:RadScriptBlock>
Nice... everything works as expected!

Freitag, 2. März 2012

Ext.Net V2 - Already worth to check it out! (And a late happy new year)

This Blog moved to http://webapps-in-action.com/

It's been some weeks now, since the Ext.Net Team released the first Developer Preview of
their .NET/ExtJS Framework.

Not ready for productive yet (ExtJS 4.1 is still too "buggySlow" with IE (I know but's enterprises policy)) - anyway have a look at the amazing work done so far.


http://examples2.ext.net/

Hopefully some day, Sencha Touch will be also part of Ext.Net
In word of Sencha Touch.. With Windows 8 Consumer Preview and the "Chromeless" IE10 Platform Preview 5, there still a lot of work to do. I've tested KendoUI, Sencha Touch and jQuery Mobile. As expected, HTML5 isn't HTML5.

Mittwoch, 21. Dezember 2011

Telerik RadSlidingPane Title Alignment/Rotate CSS3 Fix for in IE, Chrome, etc.

This Blog moved to http://webapps-in-action.com/

I faced the same problem like in this Telerik Forum Post:
http://www.telerik.com/community/forums/aspnet/splitter/radslidingpane-title-alignment-in-mozilla-firefox.aspx

RadSlidingZone / RadSlidingPane title alignment for Chrome, Firefox as the alignement in Internet Explorer? Telerik Support: "Feature is available only for the IE browser. Prepare images with the text" WTF?, Kidding me?

Having a picture for every language for every RadSlidingPane all over the application. Yes you could generate the pictures on the fly, but i thing this unnecessary! Just some CSS an a little bit of jQuery to get the job done - Have a look at this solution:



Slider.aspx Page
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Slider.aspx.cs" Inherits="RadControlsWebApp1.Slider" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head" runat="server">
    <title></title>
    <telerik:RadStyleSheetManager ID="radStyleSheetManager" runat="server" />
    <style type="text/css">
        .rspPaneTabText
        {
            writing-mode: tb-rl !important;
            -webkit-transform: rotate(-90deg) !important;
            -moz-transform: rotate(-90deg) !important;
            -o-transform: rotate(-90deg) !important;
            white-space: nowrap !important;
            height: 120px;
            width: 120px;
            filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2) !important;
            font-size: 20px !important;
            display: block !important;
        }
    </style>
</head>
<body>
    <form id="form" runat="server">
    <telerik:RadFormDecorator runat="server" Skin="Black" ID="radFormDecorator" />
    <telerik:RadScriptManager ID="radScriptManager" runat="server">
        <Scripts>
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.Core.js" />
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.jQuery.js" />
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.jQueryInclude.js" />
        </Scripts>
    </telerik:RadScriptManager>
    <telerik:RadAjaxManager ID="radAjaxManager" runat="server">
    </telerik:RadAjaxManager>
    <telerik:RadSplitter ID="radSplitter" runat="server" Width="700" Height="500">
        <telerik:RadPane ID="LeftPane" runat="server" Width="22px" Scrolling="none">
            <telerik:RadSlidingZone ID="slidingZone" runat="server" Width="22px">
                <telerik:RadSlidingPane ID="radSlidingPanelA" runat="server" Width="150px" MinWidth="100">
                    Pane 1 Content
                </telerik:RadSlidingPane>
                <telerik:RadSlidingPane ID="radSlidingPanelB" runat="server" Width="150px" MinWidth="100">
                    Pane 2 Content
                </telerik:RadSlidingPane>
                <telerik:RadSlidingPane ID="radSlidingPanelC" runat="server" Width="150px" MinWidth="100">
                    Pane 3 Content
                </telerik:RadSlidingPane>
            </telerik:RadSlidingZone>
        </telerik:RadPane>
    </telerik:RadSplitter>
    <telerik:RadScriptBlock runat="server" ID="radScriptBlock">
        <script type="text/javascript">
            // ########################
            function pageLoad() {
                $(".rspPaneTabText").html("Search");
                $(".rspPaneTabContainer").attr("title", "Search");
                $(".rspSlideTitle").html("Search");
            }
        </script>
    </telerik:RadScriptBlock>
    </form>
</body>
</html>

Highlight target area while dragging row from Telerik RadGrid

This Blog moved to http://webapps-in-action.com/

Here is a little example how to drag and drop a row from Telerik's RadGrid to a DIV while highlight on jQuery mouseenter and revert the changes on jQuery mouseleave Event. After you dropped the row on the target area, a Javascript function with the Id in the sender arguments is fired.



Default.aspx Page
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <telerik:RadStyleSheetManager ID="radStyleSheetManager" runat="server" />
    <style type="text/css">
        .dropZone
        {
            width: 200px;
            height: 200px;
            border: 1px solid #000;
            display: block;
            background-color: #999;
            text-align: center;
            font-family: "Tahoma";
            font-size: 12px;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <telerik:RadFormDecorator runat="server" Skin="Black" ID="radFOrmDecorator" />
    <telerik:RadScriptManager ID="radScriptManager" runat="server">
        <Scripts>
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.Core.js" />
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.jQuery.js" />
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.jQueryInclude.js" />
        </Scripts>
    </telerik:RadScriptManager>
    <telerik:RadAjaxManager ID="radAjaxManager" runat="server">
    </telerik:RadAjaxManager>
    <table>
        <tbody>
            <tr>
                <td>
                    <telerik:RadGrid ID="radGrid" runat="server" AllowPaging="true" BorderWidth="0">
                        <MasterTableView DataKeyNames="Id" ClientDataKeyNames="Id">
                        </MasterTableView>
                        <ClientSettings AllowColumnsReorder="true" ReorderColumnsOnClient="true" ColumnsReorderMethod="Reorder"
                            AllowRowsDragDrop="true">
                            <Selecting AllowRowSelect="true" EnableDragToSelectRows="false" />
                            <ClientEvents OnRowDropping="onRowDropping" OnRowDragStarted="onRowDragStarted" />
                            <Animation AllowColumnReorderAnimation="true" AllowColumnRevertAnimation="true" />
                        </ClientSettings>
                    </telerik:RadGrid>
                </td>
                <td valign="top">
                    <div class="dropZone" id="dropZone">
                        <br /><br />
                        DROP ZONE
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
    <telerik:RadScriptBlock runat="server" ID="radScriptBlock">
        <script type="text/javascript">
            // ########################
            function onRowDragStarted(sender, args) {
                $("#dropZone").mouseenter(function () {
                    $("#dropZone").css('background', '#FF9900');
                }).mouseleave(function () {
                    $("#dropZone").css('background', '#999');
                });
            }
            // ########################
            function onRowDropping(sender, args) {
                $("#dropZone").css('background', '#999');
                $("#dropZone").unbind('mouseenter').unbind('mouseleave');
                if (sender.get_id() == "<%=radGrid.ClientID %>") {
                    var node = args.get_destinationHtmlElement();
                    if (isChildOf('dropZone', node)) {
                        alert("You droped item " + args._dragedItems[0].getDataKeyValue('Id') + ". We can now fire an ajax update.");
                        args.set_cancel(true);
                    }
                }
                args.set_cancel(true);
            }
            // ########################
            function isChildOf(parentId, element) {
                while (element) {
                    if (element.id && element.id.indexOf(parentId) > -1) {
                        return true;
                    }
                    element = element.parentNode;
                }
                return false;
            }
        </script>
    </telerik:RadScriptBlock>
    </form>
</body>
</html>

Default.aspx.cs Class
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using System.Data;
using System.Configuration;
using System.Web.Security;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Telerik.Web.UI;
using System.Collections.Generic;

public partial class Default : System.Web.UI.Page
{

    public class Product
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public DateTime created { get; set; }
        public decimal Price { get; set; }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        List<Product> list = new List<Product>();
        for (int i = 1; i < 10; i++)
        {
            list.Add(new Product { Id = i.ToString(), created = DateTime.Now, Name = "Product " + i, Price = Convert.ToDecimal(i + 2.3) });
        }
        radGrid.DataSource = list;
        radGrid.DataBind();
    }
}

Mittwoch, 23. November 2011

JavaScript Intellisense with Telerik in ASP.NET Master Page Project with VS 2010

This Blog moved to http://webapps-in-action.com/

Today I was looking for a solution to get finally the JScript/Javascript/jQuery Intellisense feature working with my ASP.Net Webform Project to work. I found some good articles:

- JScript IntelliSense Overview

- JScript IntelliSense: A Reference for the “Reference” Tag

- Enabling JavaScript intellisense in VS.NET 2010 to work with SharePoint 2010

- Rich IntelliSense for jQuery

BUT, all of suggested solutions did not work right with my Master Page based Visual Studio 2010 Solution.
Only with physical Javascript Files (Telerik includes certain Javascript Files like jQuery as Ressource) or/and configure always a new ASP.NET Scriptmanager / RadScriptManager
on every page derived from the Master Page, wasn't exactly what I was looking for.

So I came up with the following simple Solution, to Trick VS2010 and still have the Project running with multiple runat="server" Scriptmanagers.

In short:
- New ASP.NET control derived from ScriptManager with emtpy overwritten OnInit() to use it as emtpy wrapper for VS2010.

In detail:

New RadScriptManager Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Telerik.Web.UI;

namespace IntellisenseJavascript.Controls
{
    public class IntelliJS : RadScriptManager
    {
        protected override void OnInit(EventArgs e)
        {
        }
        protected override void OnPreRender(EventArgs e)
        {            
        }
        protected override void OnLoad(EventArgs e)
        {            
        }
        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {            
        }
        public override void RenderControl(System.Web.UI.HtmlTextWriter writer)
        {            
        }
    }
}
 
web.config
<configuration>
  ...
  <system.web>
    ...
    <pages>
      <controls>
        <add tagPrefix="telerik" namespace="Telerik.Web.UI" assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"/>
        <add tagPrefix="VSFix" namespace="IntellisenseJavascript.Controls" assembly="IntellisenseJavascript"/>
      </controls>
    </pages>
    ...
 
Master Page
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="IntellisenseJavascript.Site" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head" runat="server">
    <title></title>
    <telerik:RadStyleSheetManager ID="radStyleSheetManager" runat="server" />
</head>
<body>
    <form id="form" runat="server">
    <telerik:RadScriptManager ID="radScriptManager" runat="server">
        <Scripts>
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.Core.js" />
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.jQuery.js" />
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.jQueryInclude.js" />
        </Scripts>
    </telerik:RadScriptManager>
    <telerik:RadAjaxManager ID="radAjaxManager" runat="server">
    </telerik:RadAjaxManager>
    <div>
        #MASTER CONTENT#
        <asp:ContentPlaceHolder ID="contentPlaceHolder" runat="server">
        </asp:ContentPlaceHolder>
    </div>
    </form>
    <script type="text/javascript">
        $(function () {
            // Masterpage ready
            $('body').css('margin', '50px');
        });
    </script>
</body>
</html>
 
ASPX Page
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="IntellisenseJavascript.Default" %>

<asp:Content ID="Content1" ContentPlaceHolderID="contentPlaceHolder" runat="server">
    <VSFix:IntelliJS runat="server" ID="intelliJS">
        <Scripts>
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.Core.js" />
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.jQuery.js" />
            <asp:ScriptReference Assembly="Telerik.Web.UI, Version=2011.3.1115.40, Culture=neutral, PublicKeyToken=121fae78165ba3d4"
                Name="Telerik.Web.UI.Common.jQueryInclude.js" />
        </Scripts>
    </VSFix:IntelliJS>
    <div style="border: 5px solid #FF9900;">
        #PAGE CONTENT#
    </div>
    <script type="text/javascript">
        $(function () {

            // Page ready
            $('body').css('border', '5px solid #888');

        });    
    </script>
</asp:Content> 
 
The Result
I know, this is not the way it meant to be... but now at least you can have a Main ScriptManager for all Common Scripts and Settings, inject page specific Javascripts in PageLoad Event in normal ASPX Files and have JavaScript Intellisense for defined Scripts from JS Files or Assembly Ressouce in your Content Placeholder. Maybe, vNext will fix this.



Samstag, 11. Dezember 2010

How to show Images from Database (Binary Large Objects / BLOB) within Ext.Net GridPanel using ASP.Routing

This Blog moved to http://webapps-in-action.com/

This is what we want to achieve:


We have a table on the SQL Server with several cocktails. Images are saved as blob in the record.

First of all we create Linq-To-SQL Classes (Data.dbml) to have our Cocktail table available as entity for the object mapping in our data context.



We use two ASPX Files. Default.aspx for Ext.Net and ImageLoad.aspx with ASP.NET Routing for loading Images with clean URL and content typ.
To have ASP.NET Routing working, we need also to register the necessary URL Routes in the Global.asax.cs.

Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestApp.Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <ext:XScript runat="server" ID="xScript">
        <script type="text/javascript">
            var GetImage = function (value) {
                return "<img src='images/" + value + ".jpg' />";
            }
        </script>
    </ext:XScript>
</head>
<body>
    <form id="form1" runat="server">
    <ext:ResourceManager ID="res" runat="server">
    </ext:ResourceManager>
    <div>
        <ext:GridPanel ID="GridPanel1" runat="server" StripeRows="true" Title="Cocktails"
            Width="300" Height="600" Visible="true">
            <ColumnModel ID="ColumnModel1" runat="server">
                <Columns>
                    <ext:Column ColumnID="ID" Header="Image" DataIndex="ID" Width="125">
                        <Renderer Fn="GetImage" />
                    </ext:Column>
                    <ext:Column Header="Name" DataIndex="Name">
                    </ext:Column>
                </Columns>
            </ColumnModel>
            <Store>
                <ext:Store ID="Store1" runat="server">
                    <Reader>
                        <ext:JsonReader>
                            <Fields>
                                <ext:RecordField Name="ID" />
                                <ext:RecordField Name="Name" />
                            </Fields>
                        </ext:JsonReader>
                    </Reader>
                </ext:Store>
            </Store>
        </ext:GridPanel>
    </div>
    </form>
</body>
</html>

Default.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;

namespace TestApp
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            using (DataContext dc = new DataContext())
            {
                this.Store1.DataSource = dc.Cocktails.ToList();
                this.Store1.DataBind();
            }
        }
    }
}

ImageLoad.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ImageLoad.aspx.cs" Inherits="TestApp.ImageLoad" %>

ImageLoad.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;

namespace TestApp
{
    public partial class ImageLoad : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            using (DataContext dc = new DataContext())
            {
                try
                {
                    Cocktail cocktail = (from c in dc.Cocktails
                                         where c.ID == Convert.ToInt32(Page.RouteData.Values["id"])
                                         select c).FirstOrDefault();

                    if (cocktail != null)
                    {
                        MemoryStream imageStream = new MemoryStream();

                        byte[] imageContent = cocktail.Image.ToArray();

                        imageStream.Position = 0;
                        imageStream.Read(imageContent, 0, (int)imageStream.Length);
                        Response.ContentType = "image/jpeg";
                        Response.BinaryWrite(imageContent);
                        Response.End();
                    }
                    else
                    {
                        // ERORR HANDLING NOT FOUND
                    }
                }
                catch
                {
                    // ERORR HANDLING INVALID PARAMETER, DB CONN, ETC.
                }
            }
        }
    }
}

Global.asax.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Web.Routing;

namespace TestApp
{
    public class Global : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapPageRoute("ImageLoad",
                "images/{id}.jpg",
                "~/ImageLoad.aspx");
        }
        void Application_Start(object sender, EventArgs e)
        {         
            RegisterRoutes(RouteTable.Routes);
        }
        void Application_End(object sender, EventArgs e)
        {
        }
        void Application_Error(object sender, EventArgs e)
        {
        }
        void Session_Start(object sender, EventArgs e)
        {
        }
        void Session_End(object sender, EventArgs e)
        {
        }
    }
}

Montag, 29. November 2010

Complete generic multi-language with Ext.Net

This Blog moved to http://webapps-in-action.com/

This time, I provide you an example which shows a possible way of having multi language all over your Ext.Net Project. Not quite simple, but very generic. And of course still open for improvements.

I'll maybe describe it later in detail. Now in short:

Featuring: Correct CultureInfo in Threat, Automatic replacement within markup,
IDs as Enums, Complex Translation in Codebehind / Javascript with JSON and string.Format() port...



Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="MLang._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Complete Multi-Language with Ext.NET</title>
</head>
<body>
    <ext:ResourceManager runat="server" ID="resMgr" />
    <form id="form" runat="server" style="padding: 10px">
    <ext:Panel ID="panel" runat="server" Title="_SimpleFormWithFieldSets" Width="350"
        Frame="true" ButtonAlign="Center">
        <Items>
            <ext:FieldSet runat="server" CheckboxToggle="true" Title="_UserInformation" AutoHeight="true"
                Collapsed="true" LabelWidth="75" Layout="Form">
                <Items>
                    <ext:TextField runat="server" FieldLabel="_Firstname" AllowBlank="false" AnchorHorizontal="100%" />
                    <ext:TextField runat="server" FieldLabel="_Lastname" AnchorHorizontal="100%" />
                    <ext:TextField runat="server" FieldLabel="_Company" AnchorHorizontal="100%" />
                    <ext:TextField runat="server" FieldLabel="_Email" AnchorHorizontal="100%" />
                </Items>
            </ext:FieldSet>
            <ext:FieldSet runat="server" CheckboxToggle="true" Title="_PhoneNumber" AutoHeight="true"
                LabelWidth="75" Layout="Form">
                <Items>
                    <ext:TextField runat="server" FieldLabel="_Home" AnchorHorizontal="100%" />
                    <ext:TextField runat="server" FieldLabel="_Business" AnchorHorizontal="100%" />
                    <ext:TextField runat="server" FieldLabel="_Mobile" AnchorHorizontal="100%" />
                    <ext:TextField runat="server" FieldLabel="_Fax" AnchorHorizontal="100%" />
                    <ext:TextField ID="txtMoney" runat="server" FieldLabel="_Money" AnchorHorizontal="100%" />
                    <ext:TextField ID="txtDate" runat="server" FieldLabel="_Date" AnchorHorizontal="100%" />
                    <ext:TextField ID="txtCodebehind" runat="server" AnchorHorizontal="100%" />
                </Items>
            </ext:FieldSet>
            <ext:Label runat="server" ID="labelText">
            </ext:Label>
        </Items>
        <Buttons>
            <ext:Button runat="server" Text="_Save" />
            <ext:Button runat="server" Text="_Cancel" />
            <ext:Button runat="server" Text="Javascript">
                <Listeners>
                    <Click Fn="LangWithinJS" />
                </Listeners>
            </ext:Button>
        </Buttons>
    </ext:Panel>
    <ext:Button runat="server" ID="changeLang" OnClick="changeLang_Click" AutoPostBack="true"
        Text="_ChangeLanguage">
    </ext:Button>
    </form>
    <ext:XScript runat="server" ID="xScript">
    <script type="text/javascript">                
        /* ----------------------------------------- */
        var LangWithinJS = function () {            
            alert(
                // THE LANGUAGE string.Format() MAGIC IN JS
                getLang('SomeText',getLang('Sentence'),getLang('Variables'))
             );                
        };
        /* ----------------------------------------- */        
        /* STRING FORMAT PORT - SHOULD BE PLACED IN STATIC.JS OR SIMILAR*/
        function getLang(id) {            
            var str, json = eval(#{language}.getValue());
            for (i = 0; i < json.length; i++) {
                if (json[i].Key == id) {
                    str = json[i].Value;
                    for (j = 0; j < arguments.length; j++) {
                        str = str.replace('{' + (j - 1) + '}', arguments[j]);
                    }
                    return str;
                }
            }
            return "#" + id + "#";
        }
        /* ----------------------------------------- */
    </script>
    </ext:XScript>
</body>
</html>

Default.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Globalization;
using Ext.Net;

namespace MLang
{
    public partial class _Default : BasePage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // TRANSLATION WITH ENUM / INTELLISENSE
            txtCodebehind.EmptyText = lang.translate(LangKey._Firstname);
            txtCodebehind.FieldLabel = lang.translate(LangKey._Firstname);

            // DEFAULT DATA TYPES IN C# IN CORRECT FORMAT
            txtDate.Text = DateTime.Now.ToString();
            txtMoney.Text = String.Format("{0:C}", 124404.34);

            // COMPLEX TEXT WITH VARIABLES
            labelText.Text = String.Format(lang.translate(LangKey.SomeText),
                                lang.translate(LangKey.Sentence),
                                lang.translate(LangKey.Variables));
        }
        
        // JUST A LANG SWITCH.
        protected void changeLang_Click(object sender, EventArgs e)
        {
            string currentLang = ((CultureInfo)Session["Language"]).Name;
            Session.Abandon();
            if (currentLang == "de-DE")
            {
                Response.Redirect("Default.aspx?l=en");
            }
            else
            {
                Response.Redirect("Default.aspx");
            }
        }
    }
}

BasePage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Globalization;
using System.Threading;
using Ext.Net;

namespace MLang
{
    public class BasePage : System.Web.UI.Page
    {
        // JSON STRING KEEPER
        protected Ext.Net.Hidden language { get; set; }

        protected override void OnLoad(EventArgs e)
        {
            // LANGUAGE ALREADY LOADED?
            if (Session["Language"] == null)
            {
                // SWITCH ON QUERY STRING
                if (!string.IsNullOrEmpty(Request["l"]))
                {
                    Session["Language"] = new CultureInfo("en-US");
                }
                else
                {
                    // SET DEFAULT LANGUAGE AND 
                    Session["Language"] = new CultureInfo("de-DE");
                }
                Response.Redirect("Default.aspx");
            }
            Thread.CurrentThread.CurrentCulture = (CultureInfo)Session["Language"];
            Session.LCID = ((CultureInfo)Session["Language"]).LCID;

            // CURRENT STRINGS LOADED?
            if (lang == null)
            {
                // HARD CODED TRANSLATION, ONLY FOR UNDERSTANDING.. DB SEE BELOW
                // THE "_" IN ID IS FOR MARKUP REPLACEMENT
                Language translation = new Language();
                translation.Translate = new Dictionary<string, string>();
                if (((CultureInfo)Session["Language"]).Name == "de-DE")
                {
                    translation.Translate.Add("_Save", "Speichern");
                    translation.Translate.Add("_Firstname", "Vorname");
                    translation.Translate.Add("_Lastname", "Nachname");
                    translation.Translate.Add("_Company", "Firma");
                    translation.Translate.Add("_Email", "Email");
                    translation.Translate.Add("_UserInformation", "Benutzer Information");
                    translation.Translate.Add("_SimpleFormWithFieldSets", "Einfaches Formular mit Fieldsets");
                    translation.Translate.Add("_PhoneNumber", "Telefon");
                    translation.Translate.Add("_Home", "Privat");
                    translation.Translate.Add("_Business", "Geschäftlich");
                    translation.Translate.Add("_Mobile", "Mobil");
                    translation.Translate.Add("_Fax", "Fax");
                    translation.Translate.Add("_Cancel", "Abbrechen");
                    translation.Translate.Add("_Money", "Währung");
                    translation.Translate.Add("_Date", "Datum");
                    translation.Translate.Add("_ChangeLanguage", "Sprache wechseln");
                    translation.Translate.Add("SomeText", "Zwei {1} in einem {0}");
                    translation.Translate.Add("Variables", "variablen");
                    translation.Translate.Add("Sentence", "Satz");
                }
                else
                {
                    translation.Translate.Add("_Save", "Save");
                    translation.Translate.Add("_Firstname", "First name");
                    translation.Translate.Add("_Lastname", "Last name");
                    translation.Translate.Add("_Company", "Company");
                    translation.Translate.Add("_Email", "Email");
                    translation.Translate.Add("_UserInformation", "User Information");
                    translation.Translate.Add("_SimpleFormWithFieldSets", "Simple Form with Fieldsets");
                    translation.Translate.Add("_PhoneNumber", "Phone number");
                    translation.Translate.Add("_Home", "Home");
                    translation.Translate.Add("_Business", "Business");
                    translation.Translate.Add("_Mobile", "Mobile");
                    translation.Translate.Add("_Fax", "Fax");
                    translation.Translate.Add("_Cancel", "Cancel");
                    translation.Translate.Add("_Money", "Money");
                    translation.Translate.Add("_Date", "Date");
                    translation.Translate.Add("_ChangeLanguage", "Change language");
                    translation.Translate.Add("SomeText", "This is a {0} with two {1}");
                    translation.Translate.Add("Variables", "variables");
                    translation.Translate.Add("Sentence", "sentence");
                }

                // BETTER:
                // SIMPLE SAMPLE GET FROM DB BY de-DE or en-US

                // translation.Translate = (from l in dataContext.Translation
                //                where l.language == ((CultureInfo)Session["Language"]).Name
                //                select l).ToDictionary(item => item.key, item => item.value);

                Session["LanguageTranslation"] = translation;
            }
            
            // ADD HIDDEN FIELD WITH ALL TRANSLATION STRINGS FOR JAVASCRIPT
            // HERE YOU COULD ALSO OPTIMIZE TO LOAD ONLY NEEDED ONCES
            language = new Ext.Net.Hidden();
            language.ID = "language";
            language.Text = lang.Translate.ToJSON();
            this.Page.Controls.Add(language);
            
            // LOOP ALL CONTROLS AND REPLACE ID LABLES
            UIHelper.SetControlTexts(this, lang);

            // CONTINUE WITH BASE
            base.OnLoad(e);
        }

        // SHORTCUT 
        public Language lang
        {
            get { return (Language)Session["LanguageTranslation"]; }
        }
    }
}

Extensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;

namespace MLang
{
    public static class Extensions
    {
        public static string ToJSON<T>(this T obj)
        {
            MemoryStream stream = new MemoryStream();
            try
            {
                //serialize data to a stream, then to a JSON string
                DataContractJsonSerializer jsSerializer = new DataContractJsonSerializer(typeof(T));
                jsSerializer.WriteObject(stream, obj);
                return Encoding.UTF8.GetString(stream.ToArray());
            }
            finally
            {
                stream.Close();
                stream.Dispose();
            }
        }
    }
}

Language.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MLang
{
    public enum LangKey
    {
        _Firstname,
        _Lastname,
        _Company,
        _Email,
        _UserInformation,
        _SimpleFormWithFieldSets,
        _PhoneNumber,
        _Home,
        _Business,
        _Mobile,
        _Fax,
        _Save,
        _Cancel,
        _Money,
        _Date,
        _ChangeLanguage,
        SomeText,
        Variables,
        Sentence
    }

    /// <summary>
    /// Generic Translation
    /// </summary>
    public class Language
    {
        public Dictionary<string, string> Translate { get; set; }

        /// <summary>
        /// Translates the specified language key.
        /// </summary>
        /// <param name="language">The language key.</param>
        /// <returns></returns>
        public string translate(LangKey language)
        {
            if (Translate.ContainsKey(language.ToString()))
            {
                return Translate[language.ToString()];
            }
            return "*" + language.ToString() + "*";
        }

        /// <summary>
        /// Translates the specified language key.
        /// </summary>
        /// <param name="language">The language key.</param>
        /// <returns></returns>
        public string translate(string language)
        {
            if (Translate.ContainsKey(language))
            {
                return Translate[language];
            }
            return "*" + language + "*";
        }
    }
}

UIHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using Ext.Net;

namespace MLang
{
    public class UIHelper
    {
        private const string lPrefix = "_";

        /// <summary>
        /// Sets the control texts.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="lang">The lang.</param>
        public static void SetControlTexts(Control parent, Language lang)
        {
            foreach (Control c in parent.Controls)
            {
                var tf = c as TextField;
                if (tf != null)
                {
                    if (IsTranslatedText(lang, tf.EmptyText))
                    {
                        tf.EmptyText = lang.translate(tf.EmptyText);
                    }
                    if (IsTranslatedText(lang, tf.FieldLabel))
                    {
                        tf.FieldLabel = lang.translate(tf.FieldLabel);
                    }
                }
                var bt = c as Button;
                if (bt != null)
                {
                    if (IsTranslatedText(lang, bt.Text))
                    {
                        bt.Text = lang.translate(bt.Text);
                    }
                }
                var lc = c as LiteralControl;
                if (lc != null)
                {
                    if (IsTranslatedText(lang, lc.Text))
                        lc.Text = lang.translate(lc.Text);
                }
                var lb = c as Label;
                if (lb != null)
                {
                    if (IsTranslatedText(lang, lb.Text))
                        lb.Text = lang.translate(lb.Text);
                }
                var pnl = c as Panel;
                if (pnl != null)
                {
                    if (IsTranslatedText(lang, pnl.Title))
                        pnl.Title = lang.translate(pnl.Title);
                }
                var gridPnl = c as GridPanel;
                if (gridPnl != null)
                {
                    foreach (var col in gridPnl.ColumnModel.Columns)
                    {
                        if (IsTranslatedText(lang, col.Header))
                            col.Header = lang.translate(col.Header);
                        var icc = col as ImageCommandColumn;
                        if (icc != null)
                        {
                            if (IsTranslatedText(lang, icc.Tooltip))
                                icc.Tooltip = lang.translate(icc.Tooltip);
                        }

                    }
                }
                SetControlTexts(c, lang);
            }
        }
        /// <summary>
        /// Determines whether [is translated text] [the specified lang].
        /// </summary>
        /// <param name="lang">The lang.</param>
        /// <param name="text">The text.</param>
        /// <returns>
        ///  <c>true</c> if [is translated text] [the specified lang]; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsTranslatedText(Language lang, string text)
        {
            if ((!string.IsNullOrEmpty(text)) && (text.StartsWith(lPrefix)))
            {
                if (lang.translate(text) != null)
                {
                    return true;
                }
            }
            return false;
        }
    }
}

Update #1

Here the first extension. Including a site.js and correct language
JS Locale for ExtJS on every page.

1. Put this in BasePage.cs after "this.Page.Controls.Add(language);"
// INCLUDE SITE DYNAMIC
HtmlGenericControl IncludeSite = new HtmlGenericControl("script");
IncludeSite.Attributes.Add("type", "text/javascript");

IncludeSite.Attributes.Add("src", VirtualPathUtility.ToAbsolute("~/static/js/site.js"));
Page.Header.Controls.Add(IncludeSite);

// INCLUDE CORRECT LANG JS 
HtmlGenericControl IncludeLang = new HtmlGenericControl("script");
IncludeLang.Attributes.Add("type", "text/javascript");
switch (((CultureInfo)Session["Language"]).Name)
{
    case 'de-DE':
        IncludeLang.Attributes.Add("src", VirtualPathUtility.ToAbsolute("~/static/js/lang-de.js"));

        X.Js.Call("initLanguage", lang.dict);
        break;
    default:
        IncludeLang.Attributes.Add("src", VirtualPathUtility.ToAbsolute("~/static/js/lang-en.js"));
        X.Js.Call("initLanguage", lang.dict);
    break;
}
Page.Header.Controls.Add(IncludeLang);

2. Then move the JS function getLang(id) from Default.aspx into "~/static/js/site.js"

3. Finally add the ExtJS locale as your needs

//************************************************************************************
//*** GERMAN - languagecode de *******************************************************
//*** Ext.Net Standard Texts for Controls ********************************************
//*** This is a copy of the Ext.Js translations located in 
//*** <base>Ext.Net\Ext.Net\Build\Ext.Net\extjs\src\locale\ext-lang-<languagecode>.js 
//*** Project specific modifications can be made by "param.<TextCode>" 
//************************************************************************************
function initLanguage(param) {
    if (Ext.View) {
        Ext.View.prototype.emptyText = "";
    }

    if (Ext.grid.GridPanel) {
        Ext.grid.GridPanel.prototype.ddText = "{0} Zeile(n) ausgewählt";
    }
...

//************************************************************************************
//*** ENGLISH - languagecode en ******************************************************
//*** Ext.Net Standard Texts for Controls ********************************************
//*** This is a copy of the Ext.Js translations located in 
//*** <base>Ext.Net\Ext.Net\Build\Ext.Net\extjs\src\locale\ext-lang-<languagecode>.js 
//*** Project specific modifications can be made by "param.<TextCode>" 
//************************************************************************************
function initLanguage(param) {
    /*!
    * Ext JS Library 3.3.0
    * Copyright(c) 2006-2010 Ext JS, Inc.
    * licensing@extjs.com
    * http://www.extjs.com/license
    */
    /**
    * List compiled by mystix on the extjs.com forums.
    * Thank you Mystix!
    *
    * English Translations
    * updated to 2.2 by Condor (8 Aug 2008)
    */

    Ext.UpdateManager.defaults.indicatorText = '<div class="loading-indicator">Loading...</div>';

    if (Ext.data.Types) {
        Ext.data.Types.stripRe = /[\$,%]/g;
    }
...

Update #2

There is an easier way for multi language in the markup, using the default "App_GlobalResources".
This makes sense if you don't need everything loaded dyamically from db or other source.

Just make with Visual Studio a resource file for English (LocalizedText.resx) and then for French equivelent (LocalizedText.fr.resx) for example.

Each resource files contain a Name/Value pair, eg.:

Name=lang_DeleteItem
Value=Delete selected item 

Name=lang_DeleteItem
Value=Effacez l'article choisi 

Now, the only thing to do in mark up is to point link the resource

<ext:Button ID="WCAdd" 
runat="server" Icon="TableAdd" 
MinWidth="75" 
ToolTip="<%$ Resources: LocalizedText, lang_DeleteItem %>"></ext:Button>

On each page load, the value is substituted based on the Thread.CurrentThread.CurrentCulture setting.
Same simple thing with Code behind in C#:

Button.ToolTips = Resources.LocalizedText.ResourceManager.GetString("lang_DeleteItem");

This is the simplest way in ASP.NET. Thanks and credits for this hint goes to Doug Romans (USA).

Montag, 22. November 2010

Dynamic Ext.Net Tooltip with IFrame within Grid cell

This Blog moved to http://webapps-in-action.com/

In this example a 1000ms delayed tooltip with a IFrame is loaded on mouse over event within the ID column. The IFrame href is built with store data which is loaded with current row/cell index. If you don't like IFrames, just replace it by a panel or load content async into the .innerHTML

Screenshot:


ASPX
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Tests._Default" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <ext:XScript runat="server">
        <script type="text/javascript">
        var showTip = function () {            
                var cellIndex = #{GridPanel1}.view.findCellIndex(this.triggerElement);            
                var rowIndex = #{GridPanel1}.view.findRowIndex(this.triggerElement);
                var record = #{Store1}.getAt(rowIndex);
                if(record != null && cellIndex == 0)
                {    
                    this.body.dom.innerHTML = "<iframe style='height:250px;width:250px;' src='Default.aspx?q=" + record.data.ID + "' />";
                }
                else{
                    this.hide();
                }
            };
        </script>
    </ext:XScript>
</head>
<body>
    <form id="form1" runat="server" onsubmit="return false">
    <ext:ResourceManager ID="ResourceManager1" runat="server" />
    <ext:Store runat="server" ID="Store1" RemoteSort="true" OnRefreshData="Store_RefreshData">
        <Reader>
            <ext:JsonReader IDProperty="ID">
                <Fields>
                    <ext:RecordField Name="ID" />
                    <ext:RecordField Name="NAME" />
                </Fields>
            </ext:JsonReader>
        </Reader>
        <Proxy>
            <ext:PageProxy>
            </ext:PageProxy>
        </Proxy>
    </ext:Store>
    <ext:GridPanel ID="GridPanel1" runat="server" StoreID="Store1" StripeRows="true"
        Header="false" Height="600px" Border="false" Layout="Fit">
        <LoadMask ShowMask="true" />
        <ColumnModel ID="ColumnModel1" runat="server">
            <Columns>
                <ext:Column ColumnID="ID" Header="ID" Sortable="true" DataIndex="ID">
                </ext:Column>
                <ext:Column ColumnID="NAME" Header="NAME" Sortable="true" DataIndex="NAME" Width="290px">
                </ext:Column>
            </Columns>
        </ColumnModel>
        <SelectionModel>
            <ext:RowSelectionModel ID="RowSelectionModel1" runat="server" SingleSelect="true">
            </ext:RowSelectionModel>
        </SelectionModel>
    </ext:GridPanel>
    <ext:ToolTip ID="RowTip" runat="server" Target="={GridPanel1.getView().mainBody}"
        Delegate=".x-grid3-cell" TrackMouse="true" ShowDelay="1000" Anchor="left" AutoDoLayout="true"
        AutoHeight="true" AutoWidth="true">
        <Listeners>
            <Show Fn="showTip" />
        </Listeners>
    </ext:ToolTip>
    </form>
</body>
</html>

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Ext.Net;

namespace Tests
{
    // DATA OBJECT
    public class TheObjects
    {
        public string ID { get; set; }
        public string NAME { get; set; }
    }
    // THE PAGE
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(Request["q"]))
            {
                Response.Write(DateTime.Now.ToString() + ": " + Request["q"]);
                Response.End();
            }

            if (!X.IsAjaxRequest)
            {
                // INIT DATA
                List<TheObjects> oList = new List<TheObjects>();
                for (int i = 0; i <= 150; i++)
                {
                    oList.Add(new TheObjects { ID = "ID" + i, NAME = "Name " + i });
                }
                Session["objs"] = oList;
            }
        }
        // BIND DATA WITH PROXY
        protected void Store_RefreshData(object sender, StoreRefreshDataEventArgs e)
        {
            Store1.DataSource = Session["objs"];
            Store1.DataBind();
        }
    }
}

Donnerstag, 18. November 2010

Update on last Post: Focus row in grouped Ext.Net GridPanel ...

This Blog moved to http://webapps-in-action.com/

Update
Thanks to Vladimir from Ext.Net Team, it's working now.

http://forums.ext.net/showthread.php?11018-SOLVED-Problem-with-focusRow()-on-Store.reload()-with-Firefox

Here the working code:

Screenshot


I used to love Telerik RAD Controls or jQuery UI. But, Ext.Net beats them now by far if you want to have a proper Rich Application Look & Feel.

I hope everyone notice the browser<>server round trips >> try to do this without Ext.Net or with jQuery (of course in ASP.NET/C# context) without a lots of own helpers in 179 lines ;-)

ASPX
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Tests._Default" %>

<%@ Import Namespace="System.Collections.Generic" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
    <ext:XScript runat="server">
        <script type="text/javascript">
            var submitMaterial = function ()
            {
                // ADD NEW RECORD
                Test.AfterEdit("ThisIsNew",{
                    success: function(id)
                    {   
                        // SUCCESS?
                        if(id.ErrorCode == 0)
                        {
                            // PUT PKEY INTO HIDDEN FIELD (UGLY, BUT PAGE PROXY IS LAST ASYNC)
                            #{hiddenPK}.setValue(id.PrimaryKey);                                        
                            
                            // GET THE GRID
                            var grid = #{GridPanel1};                   
                            
                            // DO FOCUS NEW ROW
                            grid.getStore().on("load", function() {    
                                if(#{hiddenPK}.getValue() != '')
                                {            
                                    var index = grid.getStore().indexOfId(#{hiddenPK}.getValue());                                
                                    grid.getSelectionModel().selectRow(index);                                           
                                    // EXCEPTION FOR THE GECKO
                                    if (Ext.isGecko && grid.getView().scrollToTopTask) {                                        
                                        grid.getView().scrollToTopTask.cancel();
                                    }                                          
                                    grid.getView().focusRow(index); 
                                }  
                            }, this, {single:true});      

                            // RELOAD THE STORE                       
                            grid.getStore().reload();                                                        
                        }                                     
                    }                    
                });
            }
        </script>
    </ext:XScript>
</head>
<body>
    <form id="form1" runat="server" onsubmit="return false">
    <ext:ResourceManager ID="ResourceManager1" runat="server" />
    <ext:Hidden ID="hiddenPK" runat="server">
    </ext:Hidden>
    <ext:Store runat="server" ID="Store1" RemoteSort="true" OnRefreshData="Store_RefreshData">
        <Reader>
            <ext:JsonReader IDProperty="ID">
                <Fields>
                    <ext:RecordField Name="ID" />
                    <ext:RecordField Name="NAME" />
                </Fields>
            </ext:JsonReader>
        </Reader>
        <AutoLoadParams>
            <ext:Parameter Name="start" Value="0" Mode="Raw" />
            <ext:Parameter Name="limit" Value="2000" Mode="Raw" />
        </AutoLoadParams>
        <Proxy>
            <ext:PageProxy>
            </ext:PageProxy>
        </Proxy>
    </ext:Store>
    <ext:GridPanel ID="GridPanel1" runat="server" StoreID="Store1" StripeRows="true"
        Header="false" Height="600px" Border="false" Layout="Fit">
        <LoadMask ShowMask="true" />
        <ColumnModel ID="ColumnModel1" runat="server">
            <Columns>
                <ext:Column ColumnID="ID" Header="ID" Sortable="true" DataIndex="ID" Align="Right"
                    Width="90px">
                </ext:Column>
                <ext:Column ColumnID="NAME" Header="NAME" Sortable="true" DataIndex="NAME" Align="Right"
                    Width="90px">
                </ext:Column>
            </Columns>
        </ColumnModel>
        <Plugins>
            <ext:GridFilters runat="server" ID="GridFilters1" Local="true">
                <Filters>
                    <ext:StringFilter DataIndex="ID" />
                    <ext:StringFilter DataIndex="NAME" />
                </Filters>
            </ext:GridFilters>
        </Plugins>
        <BottomBar>
            <ext:PagingToolbar Height="1px" ID="PagingToolBar2" runat="server" StoreID="Store1"
                PageSize="5000" DisplayInfo="true" HideRefresh="true" DisplayMsg="">
            </ext:PagingToolbar>
        </BottomBar>
        <SelectionModel>
            <ext:RowSelectionModel ID="RowSelectionModel1" runat="server" SingleSelect="true">
            </ext:RowSelectionModel>
        </SelectionModel>
        <KeyMap>
            <ext:KeyBinding>
                <Keys>
                    <ext:Key Code="DELETE" />
                </Keys>
            </ext:KeyBinding>
        </KeyMap>
    </ext:GridPanel>
    <ext:Button runat="server" ID="btnSubmit" Text="Button">
        <Listeners>
            <Click Handler="submitMaterial();" />
        </Listeners>
    </ext:Button>
    </form>
</body>
</html>

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Ext.Net;

namespace Tests
{
    // CALL BACK CONTROL OBJECT
    public class ResultObject
    {
        public int ErrorCode { get; set; }
        public string PrimaryKey { get; set; }
    }

    // DATA OBJECT
    public class TheObjects
    {
        public string ID { get; set; }
        public string NAME { get; set; }
    }

    // THE PAGE
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!X.IsAjaxRequest)
            {
                // INIT DATA
                List<TheObjects> oList = new List<TheObjects>();
                for (int i = 0; i <= 150; i++)
                {
                    oList.Add(new TheObjects { ID = "ID" + i, NAME = "Name " + i });
                }
                Session["objs"] = oList;
            }
        }

        // BIND DATA WITH PROXY
        protected void Store_RefreshData(object sender, StoreRefreshDataEventArgs e)
        {
            Store1.DataSource = Session["objs"];
            Store1.DataBind();
        }

        // ADD NEW RECORD
        [DirectMethod(Namespace = "Test")]
        public ResultObject AfterEdit(string newName)
        {
            ResultObject rObject = new ResultObject { ErrorCode = 0, PrimaryKey = "" };
            List<TheObjects> oList = (List<TheObjects>)Session["objs"];
            oList.Add(new TheObjects { ID = "ID151", NAME = newName });
            Session["objs"] = oList;
            rObject.PrimaryKey = "ID151";
            return rObject;
        }
    }
}

ORIGINAL POST

With the last Example I ran into Problems with Firefox. In IE8 and Chrome, ExtJS behaves correct. In Firefox 3.6.12, when the Grid is large with scrollbars, the row is selected, but then Firefox scrolls back to the top.

I'm still investigating it. I'll post a new sample when its working. For now
you can check out this Forum Thread where I posted a VS2010 Solution to reproduce it:
Ext.Net Forum: Problem with Store reload() or Databind()

Montag, 15. November 2010

Focus row in grouped Ext.Net GridPanel with DirectMethod Success Handler after new data binding

This Blog moved to http://webapps-in-action.com/

When you use the Grid Panel with a GroupField, the final grouping in the grid is made
by the ExtJS Client. Problem: Custom pre-sorting of your datasource doesn't matter any more.

If you bind now new data and want to focus the new added
record in the grid afterwards, you can either predict the row number (a bit difficult) or use another good
approach: The DirectMetods Success Handler.

In this Example a button fires a DirectMethod with a string parameter.
The called DirectMethod appends data to a session variable and updates the binding of the grid.
Finally the Method returns the new id to the defined success handler. The handler now checks
the grid/store for the id to set the focus.

ASPX
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Tests._Default" %>

<%@ Import Namespace="System.Collections.Generic" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<script runat="server">
    private List<object> MyData = new List<object>()
    {
        new { id="id1", group = "group1", test="test1" },
        new { id="id2", group = "group1", test="test2" },
        new { id="id3", group = "group1", test="test3" },
        new { id="id4", group = "group2", test="test1" },
        new { id="id5", group = "group2", test="test2" },
        new { id="id6", group = "group2", test="test3" },
    };

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!X.IsAjaxRequest)
        {
            Store store = this.GridPanel1.GetStore();
            store.DataSource = MyData;
            store.DataBind();
            this.Session["MyData"] = MyData;
        }
    }

    [DirectMethod(Namespace = "MyNamespace")]
    public string DataBindAndSelect(string message)
    {
        var myData = this.Session["MyData"] as List<object>;

        myData.Add(new { id = "id0", group = "group0", test = "test0" });

        Store store = this.GridPanel1.GetStore();
        store.DataSource = myData;
        store.DataBind();
        this.Session["MyData"] = myData;

        X.Msg.Notify("AjaxAction", message).Show();
        return "id0";
    }
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Ext.Net Example</title>
</head>
<body>
    <form id="Form1" runat="server">
    <ext:ResourceManager runat="server" />
    <ext:GridPanel ID="GridPanel1" runat="server" AutoHeight="true">
        <Store>
            <ext:Store runat="server" GroupField="group">
                <Reader>
                    <ext:JsonReader IDProperty="id">
                        <Fields>
                            <ext:RecordField Name="id" />
                            <ext:RecordField Name="group" />
                            <ext:RecordField Name="test" />
                        </Fields>
                    </ext:JsonReader>
                </Reader>
            </ext:Store>
        </Store>
        <columnmodel runat="server">
            <Columns>
                <ext:Column Header="ID" DataIndex="id" />
                <ext:Column Header="Group" DataIndex="group" />
                <ext:Column Header="Test" DataIndex="test" />
            </Columns>
        </columnmodel>
        <SelectionModel>
            <ext:RowSelectionModel runat="server" SingleSelect="true" />
        </SelectionModel>
        <View>
            <ext:GroupingView runat="server" HideGroupedColumn="true" />
        </View>
    </ext:GridPanel>
    <ext:Button runat="server" Text="DataBind and Select">
        <Listeners>
            <click handler="MyNamespace.DataBindAndSelect('Added new data to Session', {
                success: function(id) {
                    var index = GridPanel1.getStore().indexOfId(id);
                    GridPanel1.getSelectionModel().selectRow(index);
                    GridPanel1.getView().focusRow(index);  
                }
            });" />
        </Listeners>
    </ext:Button>
    </form>
</body>
</html>

Mittwoch, 10. November 2010

Dynamic generated SQL in Stored Procedure for paging with Ext.Net GridPanel

This Blog moved to http://webapps-in-action.com/

Basically as the title says. Just another stored procedure like in my previous post: Ext.NET GridPanel: Paging against large tables using Linq-To-SQL with Stored Procedures. Kind of part II.

The Difference: Two more parameters @WhereString and @SortString to pass on "WHERE xyz and ..." and "ORDER BY xyz, ..." to the stored procedure.

It's quite obvious, that the filtering needs to be done on MSSQL, when you only get
paged results and want to have valid total count and resultset.

This could might be a solution for you:
ALTER PROCEDURE [dbo].[sp_paging_dynamic]

@PageStart int,
@PageLimit int,
@WhereString nvarchar(max),
@SortString nvarchar(max),
@TotalCount int output

AS
BEGIN
 DECLARE @SQLCommand nvarchar(max)
 DECLARE @ParmDefinition nvarchar(500)
 SET @ParmDefinition=N'@TotalCount int OUTPUT'
 SET @SQLCommand =  
  
  'SELECT @TotalCount = count(*) FROM ' + 
  'dbo.TABLE '+@WhereString+';

   WITH ResultSet AS ' + 
   '(SELECT row_number() over(order by ' + @SortString + ') AS RowNumber, ' + 
   ' * FROM dbo.TABLE ' + @WhereString + ')
   SELECT * FROM ResultSet WHERE RowNumber BETWEEN ' + cast(@PageStart as varchar) + ' AND ' + cast(@PageLimit as varchar)

 EXEC sp_executesql @SQLCommand, @ParmDefinition, @TotalCount=@TotalCount OUTPUT

 RETURN @TotalCount
END

Dienstag, 9. November 2010

Enter quickly data into Ext.Net GridPanel with Keybinding

This Blog moved to http://webapps-in-action.com/

Ext.Net is not only a nice GUI for the Mouse. As far as your browser allows to bind keys via Javascript you can have a pretty nice fat client usability.

On the following Code Sample, I'll show how to use the INSERT, DELETE and ARROW Keys to quickly add and delete data rows. You can also do multiple selection while holding SHIFT Key just like in MS Excel.





ASPX
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!X.IsAjaxRequest)
        {
            Store store = this.GPanel.GetStore();
            store.DataSource = new System.Collections.Generic.List<object>
            {
                new { 
                    Name = "Bill Foot", 
                    Email = "bill.foot@ext.net", 
                    Start = new DateTime(2007, 2, 5), 
                    Salary = 37000, 
                    Active = true
                },
                new { 
                    Name = "Bill Little", 
                    Email = "bill.little@ext.net", 
                    Start = new DateTime(2009, 6, 13), 
                    Salary = 53000, 
                    Active = true
                }
            };
            store.DataBind();
        }
    }
</script>
<body>
    <ext:ResourceManager ID="resManager" runat="server" Theme="Gray" RemoveViewState="true" />
    <form runat="server" id="app" onsubmit="return false">
    <ext:KeyMap runat="server" Target="={Ext.isGecko ? Ext.getDoc() : Ext.getBody()}">
        <ext:KeyBinding>
            <Keys>
                <ext:Key Code="INSERT" />
            </Keys>
            <Listeners>
                <Event Handler="insertKey();" />
            </Listeners>
        </ext:KeyBinding>
    </ext:KeyMap>
    <ext:TextField runat="server" EmptyText="Enter MATNR" ID="addMatNr" AutoPostBack="false">
        <Listeners>
            <SpecialKey Fn="enterKeyPressHandler" />
        </Listeners>
    </ext:TextField>
    <ext:GridPanel ID="GPanel" runat="server" Height="500" StripeRows="true">
        <Store>
            <ext:Store ID="Store" runat="server">
                <Reader>
                    <ext:JsonReader>
                        <Fields>
                            <ext:RecordField Name="Name" />
                        </Fields>
                    </ext:JsonReader>
                </Reader>
            </ext:Store>
        </Store>
        <ColumnModel ID="ColumnModel1" runat="server">
            <Columns>
                <ext:Column Header="Name" DataIndex="Name">
                </ext:Column>
            </Columns>
        </ColumnModel>
        <SelectionModel>
            <ext:RowSelectionModel ID="RowSelectionModel1" runat="server" />
        </SelectionModel>
        <KeyMap>
            <ext:KeyBinding>
                <Keys>
                    <ext:Key Code="DELETE" />
                </Keys>
                <Listeners>
                    <Event Fn="deleteRows" />
                </Listeners>
            </ext:KeyBinding>
        </KeyMap>
    </ext:GridPanel>
    <ext:XScript runat="server" ID="scripts">     
        <script type="text/javascript">
            // ENTER KEY
            var enterKeyPressHandler = function (f, e) 
            {
                if (e.getKey() == 13) 
                {             
                    var grid = #{GPanel};
                    grid.insertRecord(0, {Name:#{addMatNr}.getValue()});                           
                    grid.getView().refresh();                    
                    grid.getSelectionModel().selectFirstRow();
                    grid.getView().focusEl.focus();
                    #{addMatNr}.setValue('');         
                    e.stopEvent();                                        
                }
           }
           // INSERT KEY
            var insertKey = function()
            {
                #{addMatNr}.focus(true);
            }
            // DELETE ROW
            var deleteRows = function () {
                var grid = #{GPanel};
                Ext.Msg.confirm('Delete Rows', 'Are you sure?', function (btn) {
                    if (btn == 'yes') {                        
                        grid.deleteSelected();                        
                    }                    
                    grid.getSelectionModel().selectFirstRow();
                    grid.getView().focusEl.focus();
                })
            }
        </script>     
    </ext:XScript>
    </form>
</body>
</html>

Ajax with Ext.Net using DirectEvent, DirectMethod, Listeners

This Blog moved to http://webapps-in-action.com/

In Ext.Net there are currently three main kinds for ajax interactions from client/browser to server/backend: DirectEvent, DirectMethod and Listeners.
This post will show you the simple usage and gives you an idea of the round trip from C# Codebehind (Objects/Parameters) via JSON to ExtJS running in Browser and back.

Let's start with the definitions from Ext.Net:

DirectMethod: A DirectMethod provides the ability to call server-side .NET Methods from client-side JavaScript code.

Sample for DirectMethod (recognized by the [DirectMethod] Annotation)
<script runat="server">
    [DirectMethod]
    public void LogCompanyInfo(string name, int count)
    {
        string template = string.Concat("{0} has approximately {1} employees.");
        string[] employees = new string[4] { "1-5", "6-25", "26-100", "100+" };        
        this.lblAjax.Text = string.Format(template, name, employees[count]);
    }
</script>
<ext:Label ID="lblAjax" runat="server" Text="AJAX ME" />
<ext:Button runat="server" Text="Submit">
    <Listeners>
        <Click Handler="Ext.net.DirectMethods.LogCompanyInfo('Ext.NET, Inc.', 0);" />
    </Listeners>
</ext:Button>

DirectEvent: A action will trigger a Ajax request to the server and return it to the browser.

Sample for DirectEvent (recognized by the DirectEventArgs in signature)
<script runat="server">
 protected void UpdateMsg(object sender, DirectEventArgs e)
 {
    X.Msg.Notify("Current Server Time: ", DateTime.Now.ToLongTimeString()).Show();
 }
</script>
<ext:Button ID="btnDirectEvent" runat="server" Text="Click Me">
<DirectEvents>
 <Click OnEvent="UpdateMsg">
  <Confirmation ConfirmRequest="true" Title="Confirm Box" Message="Want to see the server time?" />
 </Click>
</DirectEvents>
</ext:Button>  

Listeners: A "Listener" is a client-side event Handler. The event handler will call a client-side JavaScript function if the Listener has been configured.

Sample for a Listener
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        this.btnListener.Listeners.Click.Handler = "Ext.Msg.alert('Confirm', String.format('You Clicked {0}', el.id));";
    }
</script>
<ext:Button ID="btnListener" runat="server" Text="Execute Hanlder set by CodeBehind" />

Let's see with Firebug, what's happening on HTTP:

coming soon...