소개

이 시나리오에서는 페이지의 한 곳에 많은 항목들의 목록을 가지고 있으며(목록은 순서대로 혹은 계층적으로 나열되어야 한다) 이 목록에서의 항목 선택은, 이것과 관련된 모든 세부사항들은 페이지의 다른 한쪽에 보여지게 된다. 이 모든 것은 포스트백과 플리커(flicker) 없이도 사용자에게 부드럽고 좋은 느낌을 주어야 한다.

예를 들어, 웹 어플리케이션과 관련된 자산(finance)을 가지고 있다. 포트폴리오 내의 stock 스크립트에 관련된 세부사항들을 봐야한다(상당한 스크립트들이 동일한 페이지에 기재되어 있다). 지금 현재 스크립트가 선택되었고, 모든 세부사항들과 스크립트의 현재 상태는 어떠한 포스트백이나 플리커 없이 동일한 페이지에 보여진다.

여기서, 위에 언급한 시나리오는 ASP.NET 2.0에서 소개된 클라이언트 콜백 기능을 사용하면 된다. 이렇게 쉽게 사용할 수 있는 사용자정의 컨트롤 내에 삽입될 수 있는 이러한 기능과 관련된 상당한 코드를 발견했다. 더욱이, 페이지에서 콜백을 여러 번 사용한다면, 상당한 코드를 줄여야 한다. 그래서, 나는 클라이언트 콜백 기능을 사용하기 위해 "사용자 친화적인" 사용자정의 컨트를 개발하려고 한다.

배경

프로젝트에서, 다른 부분을 조작하는 곳은 범주화되어 있다(범주 내에서의 계층적 구조 때문에, 트리 뷰 컨트롤로 결정하였다). 한 곳에서의 선택(노드)을 통해, 동일한 페이지에 있는 그리드 내에 관련된 상태와 자료를 보여주려고 한다. 성능은 고객의 주요 요구 사항들 가운데 하나다. 그래서, 그 요구를 충족시키기 위해, 클라이언트 콜백 기능을 사용하기로 결정했다. 이 예제는 이러한 경험에 기반을 두고 있다.

코드사용

먼저, 사용자정의 컨트롤의 중요한 코드 부분들:

WebControl에서 상속받고, 또한 ICallbackEventHandler 에서 상속받는다.

public class MyCustomCallBackControl : WebControl, ICallbackEventHandler {}


컨트롤의
OnInit() 메서드는 재정의하였다. 컨트롤에 대한 콜백 스크립트는 다음과 같이 삽입되어 있다.

// OnInit was overriden in order to attach a callback handler to the control

protected override void OnInit(EventArgs e)

{

     base.OnInit(e);

           

     string callback = Page.ClientScript.GetCallbackEventReference(this, "input",

                       string.Concat(ID, "OnSuccess"), "context");

     Page.ClientScript.RegisterClientScriptBlock(typeof(MyCustomCallBackControl),

                       ID, string.Concat("function ", ID,

            "Callback(input, context) { ", callback, "; }"), true);

     //Above line - Script added during runtime:

     //function MyCustomCallBackControl1Callback(input, context)

     //{     

     //    WebForm_DoCallback('MyCustomCallBackControl1',

     //          input,MyCustomCallBackControl1OnSuccess,context,null,false);     

     //}

 

     //General meaning of it:

     // WebForm_DoCallback(eventTarget, eventArgument, eventCallback,

     //                    context, errorCallback, useAsync)

}


ICallbackEventHandler 인터페이스 구현과 관련된 GetCallBackResult RaiseCallBackEvent 메서드를 작성해야 한다.

// Event handler for code logic at server side on client-callback

// Event bubbling done here

public event EventHandler MyCallBackEvent;

public void RaiseCallbackEvent(string eventArgument)

{

      argumentParameter = eventArgument;

      if (MyCallBackEvent != null)

      {

          MyCallBackEvent(this, EventArgs.Empty);

      }

}

 

public string GetCallbackResult()

{

      //Returns back the output set during the callback

      return renderedOutput;

}


이제
클라이언트 콜백 사용자정의 컨트롤을 사용할 준비가 되었다.

이제는 페이지에서 컨트롤을 사용하는 방법을 보여주도록 하겠다. 페이지에 컨트롤을 갖다 놓고, 코드 비하인드 핸들러(code-behind handler) 정의하는데, 기본적으로, 서버 코드는 콜백 컨트롤이 호출될 실행된다.

// Bubbled event for Callback control placed

// One can handle the operations required during the callback out here.

protected void CallBackControl_Perform(object sender, EventArgs e)

{

    DataTable dt = RetrieveDataTable(((

      MyCustomControls.MyCustomCallBackControl)sender).ArgumentParameter);

    gvTest.DataSource = dt;

    gvTest.DataBind();

 

    //Setting of the response output for callback

    using (System.IO.StringWriter sw = new System.IO.StringWriter())

    {

        gvTest.RenderControl(new HtmlTextWriter(sw));

        ((MyCustomControls.MyCustomCallBackControl)sender).RenderedOutput = sw.ToString();

    }

}


예제에서는, 콜백 컨트롤에 노드를 바인딩하도록 하겠다. 작업은 자바스크립트를 사용하면 된다.

//Client Side callback event attached

tNode.NavigateUrl = "javascript:OnNodeClick('" + tNode.Value + "');";


이젠
사용자정의 콜백 컨트롤에 첨부된 자바스크립트 함수들의 정의를 남겨두었다. 함수들은 위에 정의된
OnNodeClick 메서드 안에 바인딩된다.

//Node Callback Click Event

function OnNodeClick(nodeID)

{        

    // Method name to call has a fixed naming convention

    // CustomCallbackControl name + "Callback"

        // Parameters to the function would be:

            // 1st : Input

            // 2nd : Context                     

    MyCustomCallBackControl1Callback(nodeID, null);

}       

 

// Function name has a fixed naming convention

// CustomCallbackControl name + "OnSuccess"

function MyCustomCallBackControl1OnSuccess(responseText)

{      

    // Based on responseText, action taken on client side    

    document.getElementById("tdGridView").style.display = "block";

    document.getElementById("gvTest").outerHTML = responseText;  

}


이제
컨트롤에 대한 사용을 마무리하였다. 자바스크립트 함수들을 위한 명명 규칙(naming conventions) 정의되었고,  이에 맞춰 작성해야 한다.

흥미로운

성능은 실제로 좋은데, 아주 간편하고 미끈한 UI는 아주 훌륭하다. 이것은 UpdatePanel과 콜백 컨트롤을 사용하여 둘 간의 비교와 차이를 배우시케 아주 좋은 경험이 된다.

이렇게 만든 사용자정의 컨트롤은 대단히 유용하다. 초보자조차도 내부동작원리에 대한 많은 지식이 없이도 사용할 수 있다(동일한 페이지에 여러 번 사용할 수 있다). 이 컨트롤은 사용하기 쉽고, 나의 웹 자산에 유용한 도구가 된다! :)



출처 :
http://www.codeproject.com/kb/ajax/call ··· rol.aspx


CustomClientCallBackEx.zip

Posted by 날개달기
사용자 삽입 이미지


들어가는

아티클은 자바스크립트 사용하여 GridView rows 확장 / 축소 기능을 보여준다. 기능을 시연하기 위해서 GridView 헤더에 Image 사용했다. Image 연속해서 누르면 GridView (row) 확장되고 축소된다.

HTML 코드 사용

GridView 내부에 TemplateField 추가하고 TemplateField. HeaderTemplate 안에 Image 추가한다. GridView 위한HTML 코드는 다음과 같다.
 

<asp:GridView ID="gvTab" BackColor="WhiteSmoke" runat="server" AutoGenerateColumns="False" GridLines="Vertical"

                                                                                             ShowFooter="True">

   <Columns>

      <asp:TemplateField>

         <HeaderStyle Width="25px" />

         <ItemStyle Width="25px" BackColor="White" />

         <HeaderTemplate>

            <asp:Image ID="imgTab" onclick="javascript:Toggle(this);" runat="server" ImageUrl="~/minus.gif"

                                                                                         ToolTip="Collapse" />

         </HeaderTemplate>

      </asp:TemplateField>

      <asp:BoundField HeaderText="n" DataField="n">

         <HeaderStyle Width="25px" />

         <ItemStyle Width="25px" />

      </asp:BoundField>

      <asp:BoundField HeaderText="sqrt(n)" DataField="sqrtn">

         <HeaderStyle Width="150px" />

         <ItemStyle Width="150px" />

      </asp:BoundField>

      <asp:BoundField HeaderText="qbrt(n)" DataField="qbrtn">

         <HeaderStyle Width="150px" />

         <ItemStyle Width="150px" />

      </asp:BoundField>

   </Columns>

   <HeaderStyle Height="25px" Font-Bold="True" BackColor="DimGray" ForeColor="White" HorizontalAlign="Center"

                                                                                    VerticalAlign="Middle" />

   <RowStyle Height="25px" BackColor="Gainsboro" HorizontalAlign="Center" VerticalAlign="Middle" />

   <AlternatingRowStyle Height="25px" BackColor="LightGray" HorizontalAlign="Center" VerticalAlign="Middle" />

   <FooterStyle BackColor="Gray" />

</asp:GridView>


onclick
event the GridView 헤더의 Image 추가한다.

<asp:Image ID="imgTab" onclick="javascript:Toggle(this);" runat="server" ImageUrl="~/minus.gif"

                                                                                          ToolTip="Collapse" />


자바스크립트
코드 사용

script tag태그 안에 다음 코드를 추가한다.

 

 <script type="text/javascript">

 var Grid = null;

 var UpperBound = 0;

 var LowerBound = 1;

 var CollapseImage = 'minus.gif';

 var ExpandImage = 'plus.gif';

 var IsExpanded = true;  

 var Rows = null;

 var n = 1;

 var TimeSpan = 25;

       

 window.onload = function()

 {

    Grid = document.getElementById('<%= this.gvTab.ClientID %>');

    UpperBound = parseInt('<%= this.gvTab.Rows.Count %>');

    Rows = Grid.getElementsByTagName('tr');

 }

       

 function Toggle(Image)

 {

    ToggleImage(Image);

    ToggleRows(); 

 }   

       

 function ToggleImage(Image)

 {

    if(IsExpanded)

    {

       Image.src = ExpandImage;

       Image.title = 'Expand';

       Grid.rules = 'none';

       n = LowerBound;

            

       IsExpanded = false;

    }

    else

    {

       Image.src = CollapseImage;

       Image.title = 'Collapse';

       Grid.rules = 'cols';

       n = UpperBound;

               

       IsExpanded = true;

    }

 }

       

 function ToggleRows()

 {

    if (n < LowerBound || n > UpperBound)  return;

                           

    Rows[n].style.display = Rows[n].style.display == '' ? 'none' : '';

    if(IsExpanded) n--; else n++;

    setTimeout("ToggleRows()",TimeSpan);

 }

 </script>


코드에서,
window.onload event 안의 전역 변수들은 초기화된다. 여기에는 가지 메소드 Toggle, ToggleImage ToggleImage 있다. Toggle 메소드는 헤더 이미지를 연속으로 누르면 호출된다. 메소드는 먼저 헤더 이미지를 반전시키고 다음에는 ToggleImage ToggleImage 메소드의 호출에 의해 GridView 열을 반전시킨다. ToggleRows 메소드는 여기에서 setTimeout 메소드를 사용하여 회귀적으로 호출된다는 점을 유의하자. GridView 열을 확대하고 축소하는 동안 가지 역동적인 효과 생성하기 위해 여기에서 회귀적으로 호출하였다.

재귀호출에서 지연을 만들기 위해서, ToggleRows 메소드의 호출을 25 밀리세컨드로 설정했다. 필요하다면 TimeSpan 값을 바꿔서 변경할 있다.
 

맺는

아티클에서는, 매끄러운 확장/축소Collapse 기능 위해 setTimeout 메소드를 사용했다.

 

 

출처 : http://www.codeproject.com/KB/webforms/GridViewExpandCollapse.aspx


Posted by 날개달기
사용자 삽입 이미지


도입

아티클은 ASP.NET으로 이미지를 처리하는 방법을 설명한다. 이미지 핸들러를 사용하는가? 가장 중요한 이유로는 깨진 이미지 링크인 빨간 x 표시를 보여주지 않는데 있다. 나는 이미지 핸들러를 생성하는데 그리고 이미지의 크기를 재조정하고 URL 쿼리 스트링 파라미터를 기반으로 이미지를 회전/상하좌우반전 하는데 중점을 두었다. 마지막으로, 닷넷 데이터 캐싱은 이미지를 처리하는 성능을 높이는데 사용되었다.


코드 사용

핸들러가 하는 번째 작업은 비트맵으로 저장된 이미지인지를, 닷넷 데이터 캐시 엔진 내에 존재하는지 점검한다. 여기에서는 핸들러를 위해 단계의 캐싱을 구현하였다. 번째 캐싱은 쿼리 스트링에 기반을 둔다. 그래서 예를 들면 URL 요청 "ImageHandler.ashx?image=images/rover.gif&width=200&height=400" URL 파라미터 "?image=images/rover.gif&width=200&height=400" 기반을 캐시를 가져야 한다. 만약 캐시가 발견된다면 이미지는 캐시에서 jpeg 렌더링되어 응답 시간과 처리능력 모두를 줄여둔다. 만약 캐시가 존재하지 않는다면 이미지는 복구와 처리를 시도하게 된다. 여기에 번째 캐싱 레이어가 존재한다. 원본 비트맵은 URP 파라미터에 기반을 두고 다양한 방식으로 요청될 있기 때문에, 원본 이미지 또한 하드 드라이브에 보다 적은 요청으로 인한 복구 후에 캐시된다.

        if (context.Cache[("ImageQueryURL-" + context.Request.QueryString.ToString())] != null)

        {

            bitOutput = (Bitmap)context.Cache[("ImageQueryURL-" + context.Request.QueryString.ToString())];

        }

 


단계에서 크기를 재조정할 이미지의 크기를 결정한다. 높이와 폭은 출력 크기를 결정하기 위해 쿼리 스트링 파라미터로 전달된다. 이번 코드 블록에서는 재조정될 이미지의 높이와 모두 전달된다.

    double inputRatio = Convert.ToDouble(bitInput.Width) / Convert.ToDouble(bitInput.Height);

   

    if (!(String.IsNullOrEmpty(context.Request["width"])) && !(String.IsNullOrEmpty(context.Request["height"])))

    {

        _width = Int32.Parse(context.Request["width"]);

        _height = Int32.Parse(context.Request["height"]);

        return true;

    }

   


이미지를
얻고 후에 이번엔 회전과 상하좌우 반전을 위해 쿼리 스트링을 다시 점검한다.

    if (String.IsNullOrEmpty(context.Request["RotateFlip"]))

    {

        return bitInput;

    }

    else if (context.Request["RotateFlip"] == "Rotate180flipnone")

    {

        bitOut.RotateFlip(RotateFlipType.Rotate180FlipNone);

    }

   


마지막으로
, 윈도우 GDI+ 엔진을 사용하여 이미지의 크기를 재조정한다. 조작(artifacting) 줄이기 위해 고화질 렌더링 모드(high quality rendering mode) 사용했으나, 결국 처리와 응답 시간의 크기와 관련된다. 다른 이유로는 데이터 캐싱은 클래스에서 중요한 부분이다.

        Bitmap resizedBitmap = new Bitmap(newWidth, newHeight, PixelFormat.Format24bppRgb);

 

        Graphics g = Graphics.FromImage(resizedBitmap);

        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;

        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;

        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

        Rectangle rectangle = new Rectangle(0, 0, newWidth, newHeight);

        g.DrawImage(inputBitmap, rectangle, 0, 0, inputBitmap.Width, inputBitmap.Height, GraphicsUnit.Pixel);

        g.Dispose();

       



코드 향상/빌드 방법

프로젝트의 목적은 간단하고 쉽게 클래스 안에 모든 모드가 포함되어 있는 이미지 핸들러를 사용하도록 하는데 있다. 모든 코드를 하나의 핸들러 클래스에 두는 것이 많은 설정에 골머리를 앓지 않고 쉽게 프로젝트를 마치도록 한다는 확신한다. 하지만, 여기엔 가지 절충안이 있다. 일반적으로, 핸들러는 IIS 정의된 파일 타입은 처리하기 어렵게 고안되어 있다. 이런 방식은 코드 프로젝트에서 쉽게 공유하지 못하므로 피하였지만, 가지 자잘한 수정사항은 남아 있다. 나는 또한 설정 변수로 noImageUrl 정의하였다. 아마도 web.config 파일 아니면 다른 설정 파일에.

마지막으로, 이미지 핸들러의 유익은 브라우저에 나타날 이미지의 실제 물리적 위치를 생각하지 않아도 다는데 있다. 이미지의 물리적 위치를 옮겨야 경우엔 다른 컴포넌트를 고려해 보라.


출처
: http://www.codeproject.com/KB/aspnet/ImageHandler.aspx


Posted by 날개달기