노무현 대통령 배너


'UpdateProgress'에 해당되는 글 1건

  1. 2007/03/26 ASP.NET 2.0 AJAX의 UpdateProgress Control Tips
 
2007/03/26 20:13

ASP.NET 2.0 AJAX의 UpdateProgress Control Tips

UpdateProgress 컨트롤은 UpdatePanel의 내용이 비동기식으로 변경될 경우에 사용자에게 보여지는 내용입니다.


보통 웹사이트를 보면 "페이지가 변경중입니다..."와 같은 비슷한 메세지를 본적이 있을 것 입니다.


UpdatePanel에서 페이지 변경이 이뤄지는데 그 작업이 오래 걸리는 작업이던지 어떤 이유에서 조금 오래 걸리는 작업이라고 할때


사용자는 가만히... 변경이 완료될 때 까지 이 페이지가 변경이 되는 것 인지 아닌지 알 수가 없습니다.


그래서 친절하게 페이지가 변경중이라는 메세지를 보여주고 완료가 되면 이 메세지를 안보이게 해줍니다.


AJAX가 나오기 이전에는 DIV태그등으로 사용자의 클릭이 있으면 DIV의 display속성을 block등으로 사용자에게 나타내고 완료가 되면


display속성을 none으로 하여 사용자에게 안보이기 했습니다.


AJAX에서는 좀 더 쉽게 이를 구현 할 수 있게 하기 위해 UpdateProgress 컨트롤을 지원해줍니다.


UpdateProgress 컨트롤의 초간단 예제를 보시겠습니다.


프로젝트로 AJAX웹 응용프로그램을 하나 생성하고 aspx페이지의 디자인 소스에 아래의 코드를 작성합니다.


       <asp:ScriptManagerID="ScriptManager1"runat="server">

       </asp:ScriptManager>

       <asp:UpdatePanelID="UpdatePanel1"runat="server">

           <ContentTemplate>

              <asp:LabelID="Label1"runat="server"Height="27px"Text="Label"Width="343px"></asp:Label>

              <br/>

              <asp:ButtonID="Button1"runat="server"Text="Button"OnClick="Button1_Click"/>

           </ContentTemplate>

       </asp:UpdatePanel>

       <asp:UpdateProgressID="UpdateProgress1"runat="server"AssociatedUpdatePanelID="UpdatePanel1">

           <ProgressTemplate>             

           <tablealign="center"id="UpdateProgressTable">

                   <tr>

                       <tdalign="center">

                          <asp:ImageID="Image1"runat="server"ImageUrl="~/image/ajax-loader.gif"/>

                       <br/>페이지 변경 중 입니다...

                       <br/>

                       <inputid="Button2"type="button"value="취소"onclick="CancelAsyncPostBack()"/>

                       </td>

                   </tr>

              </table>

              </ProgressTemplate>

       </asp:UpdateProgress>


AssociatedUpdatePanelID속성은 UpdateProgress가 어떤 UpdatePanel이 페이지 변경이 될 때 사용이 되는지 지정해줍니다.


Button1의 OnClick속성에 클릭 이벤트를 하나 주는데요. UpdateProgress의 동작을 잘 볼 수 있게 일부러 작업 시간을 임의적으로 늘립니다.


아래의 코드는 Button1의 클릭 이벤트 코드이며 Label1에 현재 날짜를 출력해주는 코드입니다.


       protectedvoidButton1_Click(objectsender,EventArgse)

        {

            System.Threading.Thread.Sleep(3000);

            Label1.Text ="현재시간은 "+DateTime.Now.ToString() +" 입니다.";

        }


Thread클래스의 Sleep메소드를 호출합니다. 이는 현재 쓰레드를 지정된 시간만큼 대기 합니다. 메소드 인자는 그 시간인데요 단위가


ms입니다. 즉, 3000은 3초가 되는 것 입니다. 이렇게 코드를 구성하여 실행을 해보면...

사용자 삽입 이미지

왼쪽과 같은 화면이 나오게 됩니다. UpdateProgress가 UpdatePanel의 하단에 위치하게 됩니다.


그런데... 저 메세지가 나오는 것은 좋은데 페이지 변경이 이뤄지는 동안 사용자가 아무 작업도


못하게 하고 싶을 수도 있습니다. 혹시 모를 오동작을 위해서 말이지요...


그래서 UpdateProgress를 화면 전체에 나타내려고 합니다. 화면 전체를 덮으려는 것이죠.


또 UpdateProgress에 취소 버튼을 두어 사용자가 언제든지 취소를 할 수 있게 하려 합니다.



먼저 화면 전체를 덮는 부분을 설명드리겠습니다.


이는 웹개발을 해본 개발자라면 쉽게 생각이 날 수도 있습니다. UpdateProgress안의 table태그의 width,height속성을 전체화면 크기로


주고 z-index의 수치를 높게 주면 전체 화면을 덮을 수 있습니다. 저도 그렇게 쉽게 생각하고 코딩에 들어갔습니다.


그러나... 사용자 브라우져 크기를 알기 위해 보통 사용하던... 자바스크립트 코드가 있습니다.


document.body.clientWidth, document.body.clientHeight 이죠....


그런데 이 값들이...이상하게 나오는 것 입니다. width는 제대로 인데 height가 이상한 값이 나옵니다.


궁금하시다면 위의 코드에서 document.body.clientHeight를 확인해보세요.


자세히 살펴본 결과 정말...정확하게 매정하게도.. body태그의 높이를 리턴합니다. 이전에 작성할 때는 아무렇지도 않았는데 말이지요..


왜 그럴까 생각하다가... 혹시 모를 <!DOCTYPE>태그를 지웠습니다. 그랬더니... 제가 원하는대로 잘나옵니다.


범인이 저 DOCTYPE이였습니다. 저 태그를 지우면 되지만 aspx를 Visual Studio에서 생성하면 자동으로 생기게 됩니다.


그리고.. 이는 버그도 아니고 오히려 웹 개발시 지켜야 할 표준이죠. 그리고 DOCTYPE의 속성을 보면 aspx페이지가 XHTML 1.0기준입니다.


그러니 document.body.clientHeight가 아닌 다른 방법으로 사용자 브라우져 크기를 알아야 합니다.


열심히 찾았지만 쉽게 답이 나오지 않았습니다. window.screen은 사용자 컴퓨터의 해상도 이므로 원하는 값이 아닙니다.


그러던 중 document.documentElement.clientHeight를 발견했습니다. 바로 테스트에 들어갔지만 이 값은 "0"이 나옵니다.


documentElement가 HTML태그인데...왜 이 값은 "0"이 나오는지 당췌 이해할 수가 없었습니다.


그래서...예전 태오님 세미나에 봤던 AJAX Control Toolkit에 ModalPopupExtender가 생각이 났습니다.


그래서 예제를 실행해보니 이 Extender는 정확하게 잘 동작했습니다. 이상하게 생각한 저는 ModalPopupExtender코드를 분석했습니다.


저 Extender의 자바스크립트 코드에서 height를 알아내는 코드가 있었습니다. 그 코드는...


       varclientBounds = CommonToolkitScripts.getClientBounds();

       varclientWidth = clientBounds.width;

       varclientHeight = clientBounds.height;


입니다. CommonToolkitScript클래스의 getClientBounds메서드입니다. 그 메서드가 어떤 객체를 리턴해주는 모양입니다.


그 객체에는 width와 height라는 속성이 있나 봅니다. 그래서 해당 메서드를 또 찾아보기로 했습니다.


CommonToolkitScript라는 이름에서 풍기는 이미지...느껴지시나요?? 뭔가...공통 모듈같은 뭐 그런 이미지...


그래서 AJAX Control Toolkit의 설치 폴더로 향하니... ExtenderBase와 Common폴더가 있습니다. 이름에서도 알 수 있듯이 Base랍니다. ㅋ


Base폴더에는 자바스크립트 파일은 딸랑 하나 있습니다. 반가운 일이죠...하나만 분석하면 되니까요....


그러나 실망스럽게도 ExtenderBase폴더는 아니였습니다. 그래서 Common폴더로 가니 4개의 파일이 있는데 이중 Common.js를


열어보기로 하고 스크롤 했습니다. 그랬더니...반갑게도 getClientBounds메서드가 절 반겨주었습니다.


Common.js의 getClientBounds의 내용을 보여드리면...


   getClientBounds :function() {

       /// <summary>

       /// Gets the width and height of the browser client window (excluding scrollbars)

       /// </summary>

       /// <returns type="Sys.UI.Bounds">

       /// Browser's client width and height

       /// </returns>


       varclientWidth;

       varclientHeight;

       switch(Sys.Browser.agent) {

           caseSys.Browser.InternetExplorer:

               clientWidth = document.documentElement.clientWidth;

               clientHeight = document.documentElement.clientHeight;

              break;

           caseSys.Browser.Safari:

               clientWidth = window.innerWidth;

               clientHeight = window.innerHeight;

              break;

           caseSys.Browser.Opera:

               clientWidth = Math.min(window.innerWidth, document.body.clientWidth);

               clientHeight = Math.min(window.innerHeight, document.body.clientHeight);

              break;

           default: // Sys.Browser.Firefox, etc.

               clientWidth = Math.min(window.innerWidth, document.documentElement.clientWidth);

               clientHeight = Math.min(window.innerHeight, document.documentElement.clientHeight);

              break;

        }

       returnnewSys.UI.Bounds(0, 0, clientWidth, clientHeight);

   },



이렇습니다. Sys.Browser.InternetExplorer... Sys.Browser클래스는 AJAX의 Client Script입니다. 이 레퍼런스는여기서 볼 수 있습니다.


어라?? 여기서도 document.documentElement.clientHeight로 알아 냅니다. 그리고 Sys.UI.Bounds클래스를 생성하여 리턴합니다.


그런데 분면 저도 저 코드를 실행해 봤지만 그 값이 0이였습니다. 헉;;; DOCTYPE태그를 빼고 테스트를 했습니다.


DOCTYPE태그가 없으면 doucment.documentElement.clientHeight의 값은 0입니다.


된장할... 아무튼 이제 저 코드를 추가하여 구현 해보겠습니다. DOCTYPE태그는 없으면 안된다는거~


그리고... 취소버튼을 눌렀을 경우도 구현 해보겠습니다. 이는 Sys.WebForms.PageRequestManager클래스의 getInstance메서드를


호출하고 리턴되는 객체의 abortPostBack() 메서드를 호출하는 것 입니다.


UpdateProgress의 스타일까지 포함한 전체 소스입니다.


<!DOCTYPEhtmlPUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<htmlxmlns="http://www.w3.org/1999/xhtml">

<headrunat="server">

  <title>제목 없음</title>

  <styletype="text/css">

           #UpdateProgressTable{

          position:absolute;

          z-index:100;

          top:0px;

          margin:0px;

          left:0px;

          background-color:#FAFAFA;

          border-width:0px;

          border-style:None;

          font-family:"돋움";

          filter:progid:DXImageTransform.Microsoft.Shadow(color=#ffffff, Direction=0, Strength=0)alpha(Opacity=60);

        }

  </style>

  <scripttype="text/javascript">

  functionUpdateProgressInit()

   {

       varprogress = $get("UpdateProgressTable");

       if(typeof(progress) =="object")

        {

            progress.style.width = getClientBounds().width;

            progress.style.height = getClientBounds().height;

        }

   }

  functionCancelAsyncPostBack()

   {

       varprm = Sys.WebForms.PageRequestManager.getInstance();

       if(prm.get_isInAsyncPostBack())

        {

            prm.abortPostBack();

        }

   }

  functiongetClientBounds()

   {

       varclientWidth;

       varclientHeight;

       switch(Sys.Browser.agent) {

       caseSys.Browser.InternetExplorer:

            clientWidth = document.documentElement.clientWidth;

            clientHeight = document.documentElement.clientHeight;

           break;

       caseSys.Browser.Safari:

            clientWidth = window.innerWidth;

            clientHeight = window.innerHeight;

           break;

       caseSys.Browser.Opera:

            clientWidth = Math.min(window.innerWidth, document.body.clientWidth);

            clientHeight = Math.min(window.innerHeight, document.body.clientHeight);

           break;

       default: // Sys.Browser.Firefox, etc.

            clientWidth = Math.min(window.innerWidth, document.documentElement.clientWidth);

            clientHeight = Math.min(window.innerHeight, document.documentElement.clientHeight);

           break;

      }

       returnnewSys.UI.Bounds(0, 0, clientWidth, clientHeight);

   }

  </script>

</head>

<body>

  <formid="form1"runat="server">

       <asp:ScriptManagerID="ScriptManager1"runat="server">

       </asp:ScriptManager>

       <asp:UpdatePanelID="UpdatePanel1"runat="server">

           <ContentTemplate>

              <asp:LabelID="Label1"runat="server"Height="27px"Text="Label"Width="343px"></asp:Label>

              <br/>

              <asp:ButtonID="Button1"runat="server"Text="Button"OnClick="Button1_Click"/>

           </ContentTemplate>

       </asp:UpdatePanel>

       <asp:UpdateProgressID="UpdateProgress1"runat="server"AssociatedUpdatePanelID="UpdatePanel1">

           <ProgressTemplate>             

           <tablealign="left"id="UpdateProgressTable">

                   <tr>

                       <tdalign="center">

                          <asp:ImageID="Image1"runat="server"ImageUrl="~/image/ajax-loader.gif"/>

                       <br/>페이지 변경 중 입니다...

                       <br/>

                       <inputid="Button2"type="button"value="취소"onclick="CancelAsyncPostBack()"/>

                       </td>

                   </tr>

              </table>

              </ProgressTemplate>

       </asp:UpdateProgress>

     <scripttype="text/javascript">

           UpdateProgressInit();

     </script>

  </form>

</body>

</html>


위의 코드로 작성하고 실행을 시켜보세요. 약간의 오차때문에 저는 스크롤바가 생기긴 했지만 화면 전체를 덮었습니다.


그리고 사용자가 브라우져를 리사이징 했을 경우는 제대로 동작하지 않습니다. 그 이유는 처음 로드시 크기를 알아내 UpdateProgress의


크기를 지정하니까요... 이를 위해서는 resize이벤트를 잡아내서 재 정의해주는 추가 작업도 필요합니다.


또한 위 방법은 IE7이상 버전에서는 좋기는 하지만 그 이하버전에서는 SELECT태그는 덮지 못합니다. 이것은 여러가지 꽁수로 해결해야 합니다.


왜 그렇게 되는지 그 이유와 해결 방법에 대해서는안재우님의 블로그에 방문해보세요.


마지막으로 만약에 DOCTYPE이 있을 수도 있고 없을 수도 있다. 이럴 경우에는 어떻게 해야 하는가에 대해 설명하겠습니다.


ControlToolkit의 Extender를 가지고 테스트를 해보면 알겠지만 Extender들은 DOCTYPE이 있거나 없어도 잘 동작한다.


그 이유는 document.documentElement.clientHeight와 document.body.clientHeight중 큰것을 사용하기 때문인데 이 코드는..


  functionUpdateProgressInit()

   {

       varprogress = $get("UpdateProgressTable");

       if(typeof(progress) =="object")

        {

            progress.style.width = Math.max(getClientBounds().width,document.body.clientWidth);

            progress.style.height = Math.max(getClientBounds().height,document.body.clientHeight);

        }

   }


이렇게 사용하면 됩니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 Comment 0

Trackback : http://www.mari.kr/trackback/12 관련글 쓰기