구글, 유튜브 등이 2010년부터 인터넷익스플로어6를 더이상 지원하지 않는다고 발표했고, 심지어 IE6의 장례까지 치루었지만, 크로스 브라우저 테스트시 광란의 CSS 버그마저 그냥 지나치는 웹디자이너/개발자는 없을 것입니다.
이 포스트는 ie의 시각적인 버그들을 잘 정리하고 있는 positioniseverything.net를 토대로 간략히 정리하면서 일부 수정하고 추가한 것입니다. 하지만 개인적인 필요에 의해 정리해둔 거라서 내용이 친절하지 않으므로, 자세한 내용은 위 사이트를 참고하시길…
IE 버그 원인과 해결책을 찾기위해 시간을 보내는 일은 매우 스트레스 받는 일이며, 궁극적인 해결이 아닌 버그 회피인지라 머릿속에 잘 남지도 않습니다. 때문에 굳이 아래 내용을 다 읽고 숙지할 필요는 없습니다. 레이아웃이 뭔가 이상하다 싶을때, 대충 height:1%, display:inline, position:relative, width:100% 중 하나를 쳐넣어보세요. 그래도 해결되지않을때 아래 내용을 참고하세요.
Duplicate Characters Bug 버전: IE6 문제: float 요소가 1개 이상 연속될때 마지막 float 요소의 문자 끝부분이 중복 출력되는 문제로, float 요소 내부에 <!– comment –>, <input type=”hidden” />, { display:none; } 와 같은 요소들이 포함되어있을때 발생. 해결: float 요소에 display:inline 적용 참고: http://positioniseverything.net/explorer/dup-characters.html
Guillotine Bug 버전: IE6, IE7 문제: float 요소의 하단이 잘리거나(IE6), float 요소를 포함하는 컨테이너 요소의 크기가 늘어나버리는(IE7) 문제 해결: float 요소를 포함하는 컨테이너 요소 바로 뒤에 clear해주는 요소를 삽입(예: <div style=”clear: both”></div>) 참고: http://positioniseverything.net/explorer/guillotine.html
——
IE6 Float Model Problem 버전: IE6 문제: float 요소 다음에 width가 선언되지 않은 non-floated 요소가 위치하면, 두 요소의 바운더리는 겹쳐야 한다. 하지만 non-floated 요소의 width가 선언되면, non-floated 요소가 float 요소 옆으로 나란히 위치하는 문제. 해결: 없다. 두 요소의 바운더리가 겹쳐져야 하는 상황을 아예 만들지 말거나, 꼭 겹쳐져야한다면 position을 사용하는 등의 다른 방법을 구사. 참고: http://positioniseverything.net/explorer/floatmodel.html
——
Three Pixel Text-Jog 버전: IE6 문제: float 요소 다음에 non-floated 요소가 위치하면, non-floated 요소의 텍스트가 3 pixel 밀리는 문제. 해결: float 요소에 {margin-right:-3px}, non-floated 요소에 {height:1%; margin-left:0} 적용 참고: http://positioniseverything.net/explorer/threepxtest.html
——
Inherited margins on form elements 버전: IE6, IE7 문제: form을 포함하는 컨테이너 요소에 margin이 적용되어있을때, 특정 input 요소들이 컨테이너의 margin을 상속받는 오류. 해결: input 요소 앞에 inline 요소를 삽입하거나, span, label 등의 컨테이너 요소로 input 요소를 감싼다. 참고: http://positioniseverything.net/explorer/inherited_margin.html
——
Line-height Bug 버전: IE6 문제: plain text 중간에 inline으로 img, input, textarea, select, object가 삽입되어있을 경우 line-height 제대로 표현하지 못하는(collapse됨) 문제. 해결: inline으로 삽입된 img, input… 요소의 상하 margin을 교정 (예: {margin:45px 0; vertical-align:middle;}) 참고: http://positioniseverything.net/explorer/lineheightbug.html
——
Border Chaos 버전: IE6 상황: block 요소가 두개 있다. 두번째 요소의 문제: 연속되는 block 요소중 두번째 이후 요소들의 margin-top이 음수이고, border가 적용되어있을때, 나타나는 광란의 버그 해결: 연속되는 block 요소들의 컨테이너(parent)에 {position: relative;} 적용 참고: http://positioniseverything.net/explorer/border-chaos.html
Unscrollable Content Bug 버전: IE6 문제: position이 relative이고 크기가 지정되지않은 컨테이너 요소가, position이 absolute이고 크기가 페이지보다 큰 컨텐츠를 포함하고 있다면 스크롤바가 나타나야 하지만 그렇지않은 문제. 해결: 컨테이너 요소에 {height:1%} 적용 참고: http://positioniseverything.net/explorer/unscrollable.html
Creeping Text Bug 버전: IE6 문제: block요소가 또 다른 block요소를 포함하고 있고, border-left, padding-bottom이 적용되어있을때, 내부의 block요소가 1px씩 좌측으로 기어들어가는 문제 해결: 외부 block요소에 {height:1%;} 적용 참고: http://positioniseverything.net/explorer/creep.html
——
기타 문제: a를 block으로 지정하면, 블록 전체가 마우스에 반응해야하는데 여전히 텍스트에만 반응하는 문제 해결: height:1%
문제: 리스트요소(li)간 간격이 발생하는 문제 해결: display: inline
문제: {position:absolute; bottom:0; right:0}인 요소가 relative인 부모의 우측하단에 위치하지 않고 전체 화면의 우측하단에 위치하는 문제 해결: height:1%
——
FireFox의 버그 문제: 배경 이미지의 위치를 bottom으로 했을 경우, 브라우저 크기보다 컨텐츠의 크기가 짧다면, 배경이미지가 컨텐츠 하단에 위치하는 버그 해결: html {height:100%}
CLR에 의해 초기화되고 나면 가비지 수집기는 개체를 저장 및 관리하기 위해 메모리 세그먼트를 할당합니다.이 메모리를 관리되는 힙이라고 하며, 이는 운영 체제의 네이티브 힙과 대조됩니다.
관리되는 각 프로세스마다 관리되는 힙이 있습니다.프로세스의 모든 스레드는 같은 힙에 개체를 할당합니다.
메모리를 예약하기 위해 가비지 수집기는 Win32 VirtualAlloc 함수를 호출하며 관리되는 응용 프로그램을 위해 한 번에 하나의 메모리 세그먼트를 예약합니다.또한 가비지 수집기는 필요에 따라 세그먼트를 예약하고 Win32 VirtualFree 함수를 호출하여 세그먼트를 해제해 운영 체제로 돌려보냅니다(세그먼트에서 개체를 지운 후).
힙에 할당되는 개체의 수가 적을수록 가비지 수집기가 할 일도 줄어듭니다.개체를 할당할 때는 15바이트만 필요한 상황에서 32바이트 배열을 할당하는 것처럼 필요 이상의 값을 사용하지 마십시오.
가비지 수집이 트리거되면 가비지 수집기는 비활성 개체에 의해 점유된 메모리를 회수합니다.회수 프로세스는 활성 개체를 압축하여 함께 이동하도록 하며, 비활성 공간이 제거되어 힙의 크기가 더 작아집니다.이로써 함께 할당된 개체가 관리되는 힙에서 함께 유지되어 집약성을 계속 유지합니다.
가비지 수집의 개입 수준(빈도와 지속 시간)은 할당 규모 및 관리되는 힙에서 남은 메모리의 크기에 따라 결정됩니다.
힙은 두 힙(대형 개체 힙과 소형 개체 힙)의 누적으로 간주할 수 있습니다.
대형 개체 힙에는 85,000바이트 이상의 개체가 포함됩니다.대형 개체 힙의 대형 개체는 일반적으로 배열입니다.인스턴스 개체의 크기가 상당히 커지는 경우는 거의 없습니다.
힙은 수명이 긴 개체와 짧은 개체를 처리할 수 있도록 세대로 구성됩니다.가비지 수집은 주로 힙에서 작은 부분만 점유하는 수명이 짧은 개체의 회수와 함께 발생합니다.힙에는 세 가지 개체 세대가 있습니다.
0세대
가장 젊은 세대이며 수명이 짧은 개체를 포함합니다.수명이 짧은 개체의 예로는 임시 변수가 있습니다.가비지 수집은 이 세대에서 가장 자주 발생합니다.
새로 할당된 개체는 새로운 개체 세대를 형성하며 대형 개체가 아닌 한 암시적으로 0세대입니다. 대형 개체인 경우 2세대 수집의 대형 개체 힙으로 이동합니다.
대부분의 개체는 0세대 가비지 수집에서 회수되며 다음 세대까지 남아 있지 않습니다.
1세대
이 세대는 수명이 짧은 개체를 포함하며 수명이 짧은 개체와 수명이 긴 개체 사이에서 버퍼 역할을 합니다.
2세대
이 세대는 수명이 긴 개체를 포함합니다.수명이 긴 개체의 예로는 프로세스의 기간 동안 유지되는 정적 데이터가 포함된 서버 응용 프로그램의 개체가 있습니다.
가비지 수집은 조건이 충족될 때 특정 세대에서 발생합니다.하나의 세대를 수집한다는 것은 해당 세대와 그보다 더 젊은 모든 세대의 개체를 수집한다는 것을 의미합니다.2세대 가비지 수집은 모든 세대의 모든 개체(즉, 관리되는 힙의 모든 개체)를 회수하므로 전체 가비지 수집이라고도 합니다.
유지 및 승격
가비지 수집에서 회수되지 않는 개체는 남은 개체라고 하며 다음 세대로 승격됩니다.0세대 가비지 수집에서 남은 개체는 1세대로 승격되고, 1세대 가비지 수집에서 남은 개체는 2세대로 승격되며, 2세대 가비지 수집에서 남은 개체는 2세대에 그대로 있습니다.
가비지 수집기는 한 세대의 잔존율이 높음을 탐지하면 해당 세대에 대한 할당 임계값을 늘려 다음 수집에서 충분한 회수 메모리 크기가 확보되도록 합니다.CLR은 응용 프로그램의 작업 집합이 너무 커지지 않도록 하는 것과 가비지 수집이 너무 많은 시간을 소요하지 않도록 하는 두 가지 우선 순위 사이에서 지속적으로 균형을 유지합니다.
임시 세대 및 세그먼트
0세대와 1세대의 개체는 수명이 짧으므로 이러한 세대를 임시 세대라고 합니다.
임시 세대는 임시 세그먼트라는 메모리 세그먼트에 할당되어야 합니다.가비지 수집기에서 획득하는 새로운 각 세그먼트는 새로운 임시 세그먼트가 되며 0세대 가비지 수집에서 남은 개체를 포함합니다.이전의 임시 세그먼트는 새로운 2세대 세그먼트가 됩니다.
임시 세그먼트에는 2세대 개체가 포함될 수 있습니다.2세대 개체는 여러 세그먼트를 사용할 수 있습니다(프로세스에 필요하고 메모리가 허용하는 한도만큼).
임시 가비지 수집에서 해제된 메모리의 크기는 임시 세그먼트의 크기로 제한됩니다.해제되는 메모리의 크기는 비활성 개체가 점유했던 공간에 비례합니다.
가비지 수집기는 관리되는 힙의 메모리만 추적하므로 관리되는 개체가 네이티브 파일 핸들을 사용하여 관리되지 않는 개체를 참조하는 경우에는 관리되는 개체를 명시적으로 해제해야 합니다.
관리되는 개체의 사용자는 개체에 사용되는 네이티브 리소스를 삭제할 수 없습니다.정리 작업을 수행하려면 관리되는 개체를 종료 가능 개체로 만들면 됩니다.종료는 개체가 더 이상 사용되지 않을 때 실행하는 정리 작업으로 구성됩니다.관리되는 개체가 소멸되면 해당 종료자 메서드에 지정된 정리 작업이 수행됩니다.
종료 가능 개체가 소멸된 것으로 확인되는 경우 해당 종료자는 정리 작업이 실행되도록 큐에 저장되지만 개체 자체는 다음 세대로 승격됩니다.따라서 개체가 가비지 수집에 의해 회수되었는지 여부를 확인하려면 이 세대에 발생하는 다음 가비지 수집(바로 다음 가비지 수집이 아닐 수도 있음)을 기다려야 합니다.
가비지 수집기는 자체 조정되며 다양한 시나리오에서 작동 가능합니다.설정할 수 있는 유일한 옵션은 작업 부하의 특징을 기반으로 하는 가비지 수집의 유형입니다.CLR은 다음 유형의 가비지 수집을 제공합니다.
모든 클라이언트 워크스테이션 및 독립 실행형 PC를 위한 워크스테이션 가비지 수집.이는 런타임 구성 스키마의 <gcServer> 요소에 대한 기본값입니다.
워크스테이션 가비지 수집은 동시 수집 또는 비동시 수집일 수 있습니다.동시 가비지 수집의 경우 가비지 수집 중 관리되는 스레드가 작업을 계속 수행할 수 있습니다.
.NET Framework 버전 4부터 백그라운드 가비지 수집이 동시 가비지 수집을 대체합니다.
높은 처리 속도 및 확장성이 필요한 서버 응용 프로그램을 위한 서버 가비지 수집
다음 그림에서는 서버에서 가비지 수집을 수행하는 전용 스레드를 보여 줍니다.
서버 가비지 수집
가비지 수집 구성
런타임 구성 스키마의 <gcServer> 요소를 사용하여 CLR에서 수행할 가비지 수집의 유형을 지정할 수 있습니다.이 요소의 enabled 특성을 false(기본값)로 설정하면 CLR은 워크스테이션 가비지 수집을 수행합니다.enabled 특성을 true로 설정하면 CLR은 서버 가비지 수집을 수행합니다.
동시 가비지 수집은 런타임 구성 스키마의 <gcConcurrent> 요소를 통해 지정됩니다.기본 설정은 enabled입니다.동시 가비지 수집은 워크스테이션 가비지 수집에서만 사용할 수 있으며 서버 가비지 수집에는 아무런 영향을 미치지 않습니다.
관리되지 않는 호스팅 인터페이스를 통해 서버 가비지 수집을 지정할 수도 있습니다.ASP.NET 및 Microsoft SQL Server 2005은(는) 응용 프로그램이 이러한 환경 내에서 호스팅되는 경우 서버 가비지 수집을 자동으로 활성화합니다.
워크스테이션 가비지 수집과 서버 가비지 수집을 비교할 때 고려할 사항
워크스테이션 가비지 수집에는 다음과 같은 스레딩 및 성능 고려 사항이 있습니다.
수집은 가비지 수집을 트리거한 사용자 스레드에서 발생하여 동일한 우선 순위를 유지합니다.사용자 스레드는 일반적으로 보통 우선 순위로 실행되므로 보통 우선 순위 스레드에서 실행되는 가비지 수집기는 다른 스레드와 CPU 시간을 두고 경쟁해야 합니다.
네이티브 코드를 실행하는 스레드는 일시 중단되지 않습니다.
프로세서가 하나뿐인 컴퓨터에서는 <gcServer> 설정에 관계없이 항상 워크스테이션 가비지 수집이 사용됩니다.서버 가비지 수집을 지정하는 경우 CLR은 동시성이 비활성화된 워크스테이션 가비지 수집을 사용합니다.
서버 가비지 수집에는 다음과 같은 스레딩 및 성능 고려 사항이 있습니다.
수집은 THREAD_PRIORITY_HIGHEST priority level에서 실행되는 여러 전용 스레드에서 발생합니다.
가비지 수집을 수행하기 위한 전용 스레드와 힙이 각 CPU에 제공되며, 힙은 동시에 수집됩니다.각 힙에는 소형 개체 힙과 대형 개체 힙이 포함되며 모든 힙은 사용자 코드에서 액세스할 수 있습니다.서로 다른 힙의 개체는 상호 참조할 수 있습니다.
여러 가비지 수집 스레드가 함께 작동하므로 같은 크기의 힙에서 서버 가비지 수집이 워크스테이션 가비지 수집에 비해 더 빠릅니다.
서버 가비지 수집은 세그먼트가 큰 경우가 많습니다.
서버 가비지 수집은 많은 리소스를 소비할 수 있습니다.예를 들어 프로세서가 네 개인 컴퓨터에서 12개의 프로세스가 실행 중이고 이러한 프로세스가 모두 서버 가비지 수집을 사용하는 경우 전용 가비지 수집 스레드는 48개가 됩니다.메모리 부하가 높은 상태에서 모든 프로세스가 가비지 수집을 시작하면 가비지 수집기는 48개의 스레드를 예약하게 됩니다.
수백 개의 응용 프로그램 인스턴스를 실행하는 경우 동시 가비지 수집이 사용하지 않도록 설정된 워크스테이션 가비지 수집을 사용할 수 있습니다.이렇게 하면 컨텍스트 전환이 줄어들어 성능이 향상됩니다.
워크스테이션 가비지 수집에서 동시 가비지 수집을 활성화할 수 있습니다. 동시 가비지 수집을 사용하면 대부분의 가비지 수집 기간 동안 가비지 수집을 수행하는 전용 스레드와 다른 스레드가 동시에 실행될 수 있습니다.이 옵션은 2세대 가비지 수집에만 영향을 미칩니다. 0세대 및 1세대는 매우 빠르게 완료되므로 항상 비동시 수집입니다.
동시 가비지 수집을 사용하면 수집을 위한 일시 중지가 최소화되어 대화형 응용 프로그램의 응답성이 향상됩니다.동시 가비지 수집 스레드가 실행되는 대부분의 시간 동안 관리되는 스레드가 계속 실행될 수 있습니다.이로써 가비지 수집이 발생하는 동안 일시 중지 시간이 더 짧아지게 됩니다.
여러 프로세스가 실행될 때 성능을 개선하려면 동시 가비지 수집을 사용하지 않도록 설정합니다.
동시 가비지 수집은 전용 스레드에서 수행됩니다.기본적으로 CLR은 동시 가비지 수집을 활성화하여 워크스테이션 가비지 수집을 실행합니다.이는 단일 프로세서 및 다중 프로세서 컴퓨터에서 모두 마찬가지입니다.
동시 가비지 수집 동안 힙에 소형 개체를 할당하는 기능은 동시 가비지 수집이 시작될 때 임시 세그먼트에 남아 있는 개체에 의해 제한됩니다.세그먼트의 끝에 도달하면 소형 개체 할당을 수행해야 하는 관리되는 스레드가 일시 중단된 동안 동시 가비지 수집이 완료되기를 기다려야 합니다.
동시 수집 중에 개체를 할당할 수 있으므로 동시 가비지 수집의 작업 집합은 비동시 가비지 수집에 비해 약간 더 큽니다.그러나 할당하는 개체가 작업 집합의 일부가 될 수 있으므로 이는 성능에 영향을 미칠 수 있습니다.기본적으로 동시 가비지 수집은 일시 중지 시간이 짧은 대신 CPU 및 메모리를 더 소비합니다.
백그라운드 가비지 수집에서 임시 세대(0세대 및 1세대)는 2세대 수집이 진행되는 동안 필요에 따라 수집됩니다.백그라운드 가비지 수집은 동시 가비지 수집에서 자동으로 활성화되며 사용자가 설정할 부분이 없습니다.백그라운드 가비지 수집은 동시 가비지 수집을 대체합니다.백그라운드 가비지 수집은 동시 가비지 수집과 마찬가지로 전용 스레드에서 수행되며 2세대 수집에만 적용될 수 있습니다.
참고
백그라운드 가비지 수집은 .NET Framework 4 이상 버전에서만 사용할 수 있습니다.
백그라운드 가비지 수집 중의 임시 세대 수집을 포그라운드 가비지 수집이라고 합니다.포그라운드 가비지 수집이 발생하면 모든 관리되는 스레드가 일시 중단됩니다.
백그라운드 가비지 수집이 진행 중이고 0세대에 충분한 개체가 할당된 경우 CLR은 0세대 또는 1세대 포그라운드 가비지 수집을 수행합니다.전용 백그라운드 가비지 수집 스레드는 안전한 지점을 수시로 검사하여 포그라운드 가비지 수집 요청이 있는지 여부를 확인합니다.요청이 있는 경우 포���라운드 가비지 수집이 발생할 수 있도록 백그라운드 수집은 스스로를 일시 중단합니다.포그라운드 가비지 수집이 완료되고 나면 전용 백그라운드 가비지 수집 스레드와 사용자 스레드가 다시 시작됩니다.
백그라운드 가비지 수집 중에 임시 가비지 수집이 발생할 수 있으므로 백그라운드 가비지 수집은 동시 가비지 수집에 의해 적용된 할당 제한을 제거합니다.이는 백그라운드 가비지 수집이 임시 세대에서 비활성 개체를 제거할 수 있고, 필요한 경우 1세대 가비지 수집 중에 힙을 확장할 수도 있음을 의미합니다.
백그라운드 가비지 수집은 현재 서버 가비지 수집에 대해서는 사용할 수 없습니다.
다음 그림에서는 포그라운드 가비지 수집과 함께 발생하는 백그라운드 가비지 수집을 보여 줍니다.
I'm trying to scroll down 100px every time the user gets near the top of the document.
I have the function executing when the user gets close to the top of the document, but the .scrollTo function isn't working.
I put an alert after and before to check to see if it actually was the line or not that was stopping it and only the first alert goes off, here's the code:
I know I have the jquery page linked properly because I'm using many other jquery functions throughout and they all work fine. I've also tried removing the 'px' from above and it doesn't seem to make a difference.
Don't you have a JavaScript debugger? – Matthew FlaschenMay 7 at 4:17
1
Jquery itself my be working fine, but are you sure you have the scrollTo plugin linked properly? Change one of those alerts to alert($.scrollTo); – AndrewMay 7 at 5:03
If it's not working why don't you try using jQuery's scrollTop method?
$("#id").scrollTop($("#id").scrollTop()+100);
If you're looking to scroll smoothly you could use basic javascript setTimeout/setInterval function to make it scroll in increments of 1px over a set length of time.
From the base Tomcat Authentication Sequence (using JAAS you can see the changes made by both JBoss and the IIS Connector. The important bit to note here is on the ISAPI Connector DLL Sequence. Due to the dll injecting a Principal into the request for Tomcat, all of Tomcat's regular authenticator valves fail to authorize the user. This is because, by default, they check to see if a Principal already exists in the session and return if it does.
The Tomcat IIS Authenticator Sequence shows how using the Tomcat IIS Authenticator Valve makes tomcat continue the authentication and authorization. Authentication is 'checked' by verifying that the NTLM provided Principal is not-null. Authorization is performed by passing the Principal to the Security Realm. The security realm can then populate the user's Principal with the Role's the user is granted base on the implementation of the Realm.
JBoss will now be able to use any LoginModule to populate the user's roles since it's security realm (registered in Tomcat) will now be called.
1. Root 사이트가 게시 사이트 형태인 경우 ( 공동 작업 포탈이든, 게시 사이트 든..) 2. Root 사이트에 하위 Application으로 사이트를 추가하여 구성한 경우. ( 보통 가상 디렉터리 만들 때, 기능 옵션에 실행을 체크하여 넣으면 "기어" 표시가 생기면서 하위 응용 프로그램으로 구성되는 경우 )
training and education for professional developers
design patterns
C#, ASP.NET, WPF, WCF, LINQ, PATTERNS
Design Patterns
Design patterns are recurring solutions to software design problems you find again and again in real-world application development. Patterns are about design and interaction of objects, as well as providing a communication platform concerning elegant, reusable solutions to commonly encountered programming challenges.
The Gang of Four (GoF) patterns are generally considered the foundation for all other patterns. They are categorized in three groups: Creational, Structural, and Behavioral. Here you will find information on these important patterns.
To give you a head start, the C# source code is provided in 2 forms: 'structural' and 'real-world'. Structural code uses type names as defined in the pattern definition and UML diagrams. Real-world code provides real-world programming situations where you may use these patterns.
A third form, '.NET optimized' demonstrates design patterns that exploit built-in .NET 2.0, 3.0, and 3.5 features, such as, generics, attributes, delegates, object and collection initializers, automatic properties, and reflection. These and much more are available in our Design Pattern Framework 3.5TM. See our Singleton page for a .NET 3.5 Optimized code sample.
If you are using AJAX then the only way i have found yet to give an alert to a user on return to the Asynchronous post back is to add an “end request” handler to the PageRequestManager.
In this way you can tell the request manager to run a javascript function on returning from a Asynchronous post back event of AJAX.
Introduction
Most security systems' passwords are case-sensitive. Case sensitivity nearly doubles the number of possible characters that can appear in the password, which makes it harder for nefarious users trying to break into the system. As a result, if a user logging into the system has Caps Lock turned on, they'll enter letters in the opposite case and not be able to login. Because the textboxes that collect user input typically are masked to prevent an onlooker from seeing a user's password, the user typing in her password may not realize that Caps Lock is on. To help prevent this type of user error, many login screens for desktop-based applications display some sort of warning if the user's Caps Lock is on when they start typing in their password.
Unfortunately, such functionality is rarely seen in web applications. A 4Guys reader recently asked me if there was a way to provide such feedback to a user logging into an ASP.NET website. The short answer is, Yes. It is possible to detect whether a user has Caps Lock enabled through JavaScript. Likewise, it's possible to have this client-side logic execute whenever a user starts typing in a particular textbox. With a little bit of scripting, such an interface could be devised.
After thinking about the implementation a bit, I decided to create a compiled server-side control that would emit the necessary JavaScript. This article introduces this control, WarnWhenCapsLockIsOn, and shows how it works and how to use it in an ASP.NET web page. Read on to learn more!
An Overview of the WarnWhenCapsLockIsOn Control's Functionality
The WarnWhenCapsLockIsOn control is designed to display a page developer-specified message if a user types in a specified TextBox control with Caps Lock on. The control extends the Label class so it has the same set of properties as the Label control: Text, Font, Width, CssClass, and so on. Use these properties to configure the message and the message's appearance.
In addition to the Label control's properties, the WarnWhenCapsLockIsOn control also has a TextBoxControlId property, which you must set to the ID of a TextBox control. This property is required. (To provide a Caps Lock warning for multiple TextBox controls on the page, add a WarnWhenCapsLockIsOn control for each TextBox.) If the user starts typing in the specified textbox with the Caps Lock on, the WarnWhenCapsLockIsOn control is displayed. The WarnWhenCapsLockIsOn control is hidden again when one of the following conditions apply:
The user types in the same textbox with Caps Lock off.
The WarnWhenCapsLockIsOn control has been displayed for WarningDisplayTime milliseconds. The WarningDisplayTime property defaults to a value of 2500, meaning that the control will be displayed for 2.5 seconds after the last character with Caps Lock on has been typed. You can lengthen or shorten this property value as needed. A value of 0 displays the warning until the user types in a character without Caps Lock on or after there's a postback.
To provide a warning to the user if Caps Lock is on, then, you'll add this control to the page, set its Text property (and, perhaps, other formatting properties), and indicate the TextBox to "watch" via the control's TextBoxControlId property. That's all there is to it! We'll discuss how to add the WarnWhenCapsLockIsOn control to your ASP.NET website project and look at an end-to-end example in the "Using the WarnWhenCapsLockIsOn Control in an ASP.NET Web Page" section later on in this article.
Initially Hiding the WarnWhenCapsLockIsOn Control
Whenever the ASP.NET page is loaded in the user's browser, the WarnWhenCapsLockIsOn control needs to be hidden so that it does not appear. The WarnWhenCapsLockIsOn control should only appear when the user starts typing in the specified textbox with Caps Lock on. To initially hide the WarnWhenCapsLockIsOn control, I overrided the control's AddAttributesToRender method and added code that set the visibility and display style attributes to "hidden" and "none", respectively.
public class WarnWhenCapsLockIsOn : System.Web.UI.WebControls.Label
{
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
// Add a style attribute that hides this element (by default) writer.AddStyleAttribute(HtmlTextWriterStyle.Visibility, "hidden");
writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");
}
}
The effects of these style attribute settings are evinced by the markup sent to the browser. Because the WarnWhenCapsLockIsOn control extends the Label class, it renders as a <span> element with the value of its Text property rendered as the <span> element's inner content, just like the Label control. In the style attribute you can see that the visibility and display settings have been configured to hide the control:
<span id="ID" style="visibility:hidden;display:none;">Value of Text property</span>
We now need to write JavaScript that sets the visibility and display settings to "visible" and "inline" whenever the user types into the corresponding textbox with Caps Lock on.
Executing JavaScript in Response to Typing in a Textbox
It is possible to execute JavaScript in response to a variety of client-side events, such as when the document has loaded, when the user changes the selection of a drop-down list, when an input field receives (or loses) focus, or when the user presses a key. To execute client-side script in response to a user pressing a key, use the onkeypress event like so:
<input type="text" ... onkeypress="action" />
The action can be inline script or a call to a function. In addition to noting that a key has been pressed, JavaScript offers the event object, which includes information such as what key was pressed and whether or not the Shift key was depressed, among other useful tidbits. In short, we need to add an onkeypress client-side event handler to the TextBox control referenced by the WarnWhenCapsLockIsOn control's TextBoxControlId property. The event handler called must determine whether Caps Lock is on or off and show or hide the WarnWhenCapsLockIsOn control in response.
To add client-side event handlers to a server-side Web control use the Attributes collection like so:
WebControlID.Attributes["attribute"] = "value";
The above code adds the markup attribute="value" to the Web control's rendered output. Usually this sort of logic is applied during the PreRender stage of the page lifecycle. Therefore, to implement this functionality I overrode the OnPreRender method in the WarnWhenCapsLockIsOn class (which is fired during the PreRender stage) and added the following code:
public class WarnWhenCapsLockIsOn : System.Web.UI.WebControls.Label
{
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (this.Enabled)
{
/* We need to add two bits of JavaScript to the page:
* (1) The include file that has the JavaScript function to check if Caps Lock is on
* (2) JavaScript that will call the appropriate function in (1) when a key is pressed in the TextBoxControlId TextBox
*/
// (1) Register the client-side function using WebResource.axd (if needed)
if (this.Page != null && !this.Page.ClientScript.IsClientScriptIncludeRegistered(this.GetType(), "skmControls2"))
this.Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "skmValidators", this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "skmControls2.skmControls2.js"));
The GetTextBoxControl method (not shown in this article) returns a reference to the TextBox control specified by the TextBoxControlId property. This referenced TextBox has its Attributes collection updated to include an onkeypress event handler that calls the JavaScript function skm_CheckCapsLock. The skm_CheckCapsLock function (which we'll examine shortly), is passed three input parameters:
event - information about the keypress event, including what key was pressed and whether the Shift key was depressed.
The value of the WarnWhenCapsLockIsOn's ClientID property - we need to supply the client-side id of the WarnWhenCapsLockIsOn control so that it can be displayed or hidden, depending on whether the user has Caps Lock on.
WarningDisplayTime - the number of milliseconds to display the Caps Lock warning; defaults to 2500 milliseconds (2.5 seconds).
The WarnWhenCapsLockIsOn control is one of a few controls in my open-source control library, skmControls2. Another control in the same library is the TextBoxCounter control, which interactively displays how many characters the user has currently typed into a given textbox. (For more information on the TextBoxCounter control, read: Creating a TextBox Word / Character Counter Control.) The TextBoxCounter control requires a bit of JavaScript, and that JavaScript is already defined in a file named skmControls2.js. This file is compiled as an embedded resource in the assembly and is loaded into the ASP.NET page on which the TextBoxCounter or WarnWhenCapsLockIsOn controls are used via a call to the GetWebResourceUrl method. For more information on this technique see: Accessing Embedded Resources through a URL using WebResource.axd.
Because the skmControls2 library already has a JavaScript file, I decided to place the skm_CheckCapsLock function (and ancillary helper functions) there. It is injected into the ASP.NET page in the OnPreRender method (see step 1 in the comments in OnPreRender).
Determining Whether Caps Lock is On
When the keypress event is raised in client-side script, the event object contains information as to what key was pressed and whether the Shift key was depressed (along with whether Alt and Ctrl were depressed). It does not, however, indicate whether Caps Lock was on. The good news is that with a bit of code we can determine whether Caps Lock is on. If the user has entered a capital letter and the Shift key is not depressed, then Caps Lock must be on; likewise, if the user enters a lowercase letter and the Shift key is depressed, then Caps Lock is on.
The skm_CheckCapsLock function determines whether Caps Lock is on and, if so, calls the skm_ShowCapsWarning function, which displays the WarnWhenCapsLockIsOn control for a specified interval. If Caps Lock is not on then the skm_HideCapsWarning function is called, which hides the WarnWhenCapsLockIsOn control. The skm_CheckCapsLock function uses a modified version of a script created by John G. Wang, available online at http://javascript.internet.com/forms/check-cap-locks.html.
function skm_CheckCapsLock( e, warnId, dispTime ) {
var myKeyCode = 0;
var myShiftKey = e.shiftKey;
if ( document.all ) {
// Internet Explorer 4+
myKeyCode = e.keyCode;
} else if ( document.getElementById ) {
// Mozilla / Opera / etc.
myKeyCode = e.which;
}
if ((myKeyCode >= 65 && myKeyCode <= 90 ) || (myKeyCode >= 97 && myKeyCode <= 122)) {
if (
// Upper case letters are seen without depressing the Shift key, therefore Caps Lock is on
( (myKeyCode >= 65 && myKeyCode <= 90 ) && !myShiftKey )
||
// Lower case letters are seen while depressing the Shift key, therefore Caps Lock is on
( (myKeyCode >= 97 && myKeyCode <= 122 ) && myShiftKey )
)
{
skm_ShowCapsWarning(warnId, dispTime);
}
else {
skm_HideCapsWarning(warnId);
}
}
}
The keen reader will note that this check doesn't really identify when Caps Lock is on or off. Rather, it identifies if Caps Lock is on or off when typing in an alphabetic character. If the user types in the number "9" or presses the left arrow, the first conditional statement will evaluate to false. In other words, the skm_ShowCapsWarning or skm_HideCapsWarning functions are only called when the user enters an alphabetic character.
The skm_ShowCapsWarning and skm_HideCapsWarning functions are not shown in this article; you'll have to download the source code to inspect them. They're both fairly straightforward: both reference the WarnWhenCapsLockIsOn control and then set the visibility and display properties to show or hide the warning message. The only trickiness deals with the code that displays the warning for at most a specified number of milliseconds after the user types in with Caps Lock on. Specifically, these functions use the JavaScript setTimeout and clearTimeout methods. For more information on these functions, see the JavaScript Timing Events tutorials at W3 Schools.
Using the WarnWhenCapsLockIsOn Control in an ASP.NET Web Page
The download available at the end of this article includes the complete source code for the WarnWhenCapsLockIsOn controls, as well as a demo ASP.NET website. To use the skmControls2 controls in an ASP.NET website, copy the DLL to the website's /Bin directory and then add the following @Register directive to the tops of the .aspx pages where you want to use the controls:
The demo included in the download has an ASP.NET page that shows using the WarnWhenCapsLockIsOn control in two scenarios: on a stand-alone TextBox and on the Password TextBox in the Login control. Let's look at the stand-alone TextBox example first:
<b>Please enter your name:</b>
<asp:TextBox ID="YourName" runat="server"></asp:TextBox>
<skm:WarnWhenCapsLockIsOn runat="server" ID="WarnOnYourName"
CssClass="CapsLockWarning" TextBoxControlId="YourName"
WarningDisplayTime="5000"><b>Warning:</b> Caps Lock is on.</skm:WarnWhenCapsLockIsOn>
The declarative markup above shows three controls: a TextBox (YourName); a WarnWhenCapsLockIsOn control; and a Button Web control. The WarnWhenCapsLockIsOn control is configured to display the message "Warning: Caps Lock is on" when the user types into the YourName textbox with Caps Lock on. It uses the CSS class CapsLockWarning for its styling, which I've defined on the page; the class specifies a light yellow background, padding, centered text, and a red border. The warning message is displayed for (at most) five seconds.
The following screen shot shows the demo page in action. Note that when Caps Lock is on and an alphabetic character is typed, the warning is displayed. This warning immediately disappears when Caps Lock is turned off and another alphabetic character is typed, or after five seconds, whatever comes first.
This demo also shows how to apply the WarnWhenCapsLockIsOn control to the Password TextBox of a Login control. Start by adding a Login control to the page and then, from the Designer, select the "Convert to Template" from its smart tag. This will convert the Login control into a template, at which point you can add the WarnWhenCapsLockIsOn control in the Login user interface as needed, referencing the Password TextBox (whose ID property is "Password"). Be sure to put the WarnWhenCapsLockIsOn control within the Login control's template so that it can "see" the Password TextBox.
참고 Visual Basic .NET에서 클래스를 만드는 데 대한 배경 설명을 보려면 클래스의 이해를 참조하십시오. Visual C# .NET의 경우에는 클래스를 참조하십시오.
템플릿 클래스를 만들려면
System.Web.UI 네임스페이스의 ITemplate 인터페이스를 구현하는 새 클래스를 만듭니다.
필요한 경우, 클래스에서 만들 템플릿의 형식(ItemTemplate, AlternatingItemTemplate 등)을 결정할 때 사용되는 값을 클래스의 생성자에 전달합니다.
팁 생성자에 템플릿 형식을 전달할 때 형식 안전성을 유지하려면 ListItemType 형식을 사용하여 생성자에 매개 변수를 추가하면 됩니다. ListItemType 열거형에서는 Repeater, DataList 및 DataGrid 컨트롤에 대해 사용 가능한 템플릿 형식을 정의합니다.
ITemplate 인터페이스의 유일한 멤버인 InstantiateIn 메서드를 클래스에 구현합니다. 이 메서드를 통해 텍스트 및 컨트롤의 인스턴스를 지정된 컨테이너에 삽입할 수 있습니다.
InstantiateIn 메서드에서 템플릿 항목에 대한 컨트롤을 만들고 속성을 설정한 다음 부모의 Controls 컬렉션에 추가합니다. InstantiateIn 메서드에 전달된 참조를 통해 부모 컨트롤에 액세스할 수 있습니다.
참고Controls 컬렉션에 정적 텍스트를 직접 추가할 수는 없지만, Literal 컨트롤 또는 LiteralControl 컨트롤 등의 컨트롤을 만들고 Text 속성을 설정한 다음 이 컨트롤을 부모 컬렉션에 추가할 수는 있습니다.
다음 예제에서는 일부 정적 텍스트("Item number:") 및 카운터를 표시하는 완전한 템플릿 클래스를 보여 줍니다. 카운터는 클래스에 대해 itemcount라는 공유 또는 정적 값(사용하는 언어에 따라 다름)으로 보존되며 새 항목이 만들어질 때마다 크기가 증가합니다.
클래스에서는 만들어지는 템플릿의 형식을 나타내기 위해 ListItemType 열거형 값을 받는 명시적 생성자를 정의합니다. 만들어지는 템플릿의 형식에 따라 코드에서 서로 다른 종류의 컨트롤이 만들어지고 이 컨트롤은 부모 컨트롤의 Controls 컬렉션에 추가됩니다. 마지막으로, 대체 항목 템플릿에 대해 서로 다른 배경색을 가진 HTML 테이블이 만들어집니다.
' Visual Basic
Private Class MyTemplate
Implements ITemplate
Shared itemcount As Integer = 0
Dim TemplateType As ListItemType
Sub New(ByVal type As ListItemType)
TemplateType = type
End Sub
Sub InstantiateIn(ByVal container As Control) _
Implements ITemplate.InstantiateIn
Dim lc As New Literal()
Select Case TemplateType
Case ListItemType.Header
lc.Text = "<TABLE border=1><TR><TH>Items</TH></TR>"
Case ListItemType.Item
lc.Text = "<TR><TD>Item number: " & itemcount.ToString _
& "</TD></TR>"
Case ListItemType.AlternatingItem
lc.Text = "<TR><TD bgcolor=lightblue>Item number: " _
& itemcount.ToString & "</TD></TR>"
Case ListItemType.Footer
lc.Text = "</TABLE>"
End Select
container.Controls.Add(lc)
itemcount += 1
End Sub
End Class
// C#
public class MyTemplate : ITemplate
{
static int itemcount = 0;
ListItemType templateType;
public MyTemplate(ListItemType type)
{
templateType = type;
}
public void InstantiateIn(System.Web.UI.Control container)
{
Literal lc = new Literal();
switch( templateType )
{
case ListItemType.Header:
lc.Text = "<TABLE border=1><TR><TH>Items</TH></TR>";
break;
case ListItemType.Item:
lc.Text = "<TR><TD>Item number: " + itemcount.ToString() +
"</TD></TR>";
break;
case ListItemType.AlternatingItem:
lc.Text = "<TR><TD bgcolor=lightblue>Item number: " +
itemcount.ToString() + "</TD></TR>";
break;
case ListItemType.Footer:
lc.Text = "</TABLE>";
break;
}
container.Controls.Add(lc);
itemcount += 1;
}
}
' Visual Basic
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Repeater1.HeaderTemplate = New MyTemplate(ListItemType.Header) Repeater1.ItemTemplate = New MyTemplate(ListItemType.Item) Repeater1.AlternatingItemTemplate = _ New MyTemplate(ListItemType.AlternatingItem) Repeater1.FooterTemplate = New MyTemplate(ListItemType.Footer)
SqlDataAdapter1.Fill(DsCategories1)
Repeater1.DataBind()
End Sub
// C#
private void Page_Load(object sender, System.EventArgs e)
{
Repeater1.HeaderTemplate = new MyTemplate(ListItemType.Header); Repeater1.ItemTemplate = new MyTemplate(ListItemType.Item); Repeater1.AlternatingItemTemplate = new MyTemplate(ListItemType.AlternatingItem); Repeater1.FooterTemplate = new MyTemplate(ListItemType.Footer);
sqlDataAdapter1.Fill(dsCategories1);
Repeater1.DataBind();
}
템플릿에 데이터 바인딩 추가
템플릿 클래스 안에서 데이터에 액세스할 수 있는 방법은 클래스를 만드는 방법에 따라 다양합니다. 이 중에서 페이지 아키텍처 자체에서 데이터 바인딩을 구현하는 것이 가장 좋습니다. 템플릿에 컨트롤을 추가하면 이러한 컨트롤의 DataBinding 이벤트에 대한 처리기도 추가됩니다. 이 이벤트는 템플릿 항목과 해당 템플릿과 관련된 컨트롤이 모두 만들어진 후에 발생하며 데이터를 페치하여 컨트롤에서 사용할 수 있도록 합니다.
참고 템플릿에 컨트롤을 만들 때 디자인 타임에 템플릿을 정의할 때처럼 데이터 바인딩 식을 문자열로 포함할 수 없습니다. 데이터 바인딩 식은 템플릿이 만들어지기 전에 발생하는 페이지 처리 과정 단계에서 코드로 변환되기 때문입니다.
DataBinding 이벤트에 대한 처리기에서 컨트롤의 내용을 조작할 수 있습니다. 필수적인 것은 아니지만, 보통 임의의 위치에서 데이터를 페치(fetch)하여 컨트롤의 Text 속성에 할당합니다.
참고 Web Forms 페이지에서의 데이터 바인딩 작업에 대한 배경 설명은 Web Forms 데이터 바인딩을 참조하십시오.
동적 템플릿에 데이터 바인딩을 추가하려면 다음 작업을 수행해야 합니다.
템플릿에서 만든 컨트롤에 데이터 바인딩 이벤트 처리기를 추가합니다.
바인딩 대상이 되는 처리기를 만듭니다. 바인딩할 데이터를 이 처리기에서 가져온 다음, 바인딩되는 컨트롤의 해당 속성에 할당합니다.
데이터 바인딩 이벤트 처리기를 추가하려면
동적 템플릿에 컨트롤을 만든 후에 표준 명령을 사용하여 컨트롤의 DataBinding 이벤트를 나중에 만들 메서드에 바인딩합니다.
' Visual Basic
Dim lc As New Literal()
Case ListItemType.Item
lc.Text = "<TR><TD>"
AddHandler lc.DataBinding, AddressOf TemplateControl_DataBinding
// C#
Literal lc = new Literal();
case ListItemType.Item:
lc.Text = "<TR><TD>";
lc.DataBinding += new EventHandler(TemplateControl_DataBinding);
break;
이 예제에서 리터럴 컨트롤의 Text 속성에 추가하는 텍스트는 이전의 예제에 나온 텍스트와는 다릅니다. 여기에는 표 행의 시작 부분과 항목 템플릿에 대한 셀만 포함되어 있습니다. 데이터 바인딩 이벤트 처리기에서 셀과 행을 완성합니다.
다음 단계에서는 컨트롤에 데이터가 바인딩될 때 호출되는 이벤트 처리기를 만듭니다.
DataBinding 이벤트에 대한 처리기를 만들려면
템플릿 클래스를 구성하며 이 클래스의 InstantiateIn 등과 같은 다른 메서드와 동일한 수준의 역할을 수행할 메서드를 만듭니다. 처리기의 이름은 이벤트 처리기를 바인딩할 때 사용했던 이름과 같아야 합니다. 이 이벤트에는 다음과 같은 서명이 있어야 합니다.
Object Serialization is a process through which an object's state is transformed into some serial data format, such as XML or a binary format. For example, you can serialize an object and transport it over the Internet using HTTP between a client and a server. On the other end, deserialization reconstructs the object from the stream. XML was designed to be a technology for data exchange across heterogeneous systems. It can be easily transmitted between distributed components because of its platform independence and its simple, text-based, self-describing format. In this article I will discuss the serialization of custom business objects to XML and reconstruction the object back from the XML string, using C#. This process is called XML serialization and is very well supported by .Net.
Some good uses for serialization/deserialization include:
Storing user preferences in an object.
Maintaining security information across pages and applications.
Modification of XML documents without using the DOM.
Passing an object from one application to another.
Passing an object from one domain to another.
Passing an object through a firewall as an XML string.
These are only a few of the many possibilities that serialization opens up for us.
Suppose we have an object defined and instantiated as shown below:
class Person
{
private String personName;
private Int32 personAge;
public String Name
{
get { return personName; }
set { personName = value; }
}
public Int32 Age
{
get { return personAge; }
set { personAge = value;}
}
}
Instantiation of the class is shown below:
Person oPerson = new Person();
oPerson.Name = "Anthony";
oPerson.Age = 38;
Let's say that for some reason, we wanted to save a copy of this object just as it is at this very moment. We could serialize it as an XML document that would look something like this:
Anthony38
Then, at some later time when we needed to use the object again, we could just deserialize it and have our object restored to us just as it was at the moment we serialized it. However XML serialization follows certain rules and the classes defined by us have to conform to these rules.
1.
XML serialization serializes only the public fields and property values of an object into an XML stream
2.
XML serialization does not include type information
3.
XML serialization requires a default constructor to be declared in the class that is to be serialized
4.
XML serialization requires all properties that are to be serialized as read write properties. Read only properties are not serialized.
Serialize class
The .Net framework provides a rich collection of classes for serialization. As we are concerned with XML serialization, we will examine the use of the XmlSerializer class. This is the central class for XML serialization and its most important methods are the Serialize and Deserialize methods. Most of the articles discussing XML serialization use IO streams for saving serialized data into XML files, however in my example I will demonstrate the use of XML serialization to convert an object into XML string and store it in a memory stream instead of an XML file. This technique is extremely useful in cases where a complex object is to be converted to XML string and its properties have to be manipulated and then the object recreated from the XML string on the fly!!
Before we start with the process of demonstrating the technique of XML serialization let us have a small discussion on Metadata Attributes. A complete discussion of Metadata Attributes is beyond the scope of this article. Nevertheless we need to understand the concepts behind metadata attributes to understand how XML serialization works. Attributes are annotations to an interface or a class definition to specify certain behavior. Assemblies, classes, fields and methods, each can have attributes. Some are used by the compiler, some are used by the runtime, e.g. to identify a method requires a call to a web service, or how to serialize a class to XML. There is very little overhead associated when using attributes. Attaching attributes to a class is done directly in the source code.
The syntax to initialize a metadata attribute and attach
it to a class or a method in C# is either:
[Attribute(constructor-parameters-list )]
or:
[Attribute(constructor-parameters-list, property=value, ... )]
// The System.Xml.Serialization namespace introduces a set of attributes
// to control how classes are mapped to XML. Let's look at a quick example:
// One of the attributes used with XML serialization is the XmlRootAttribute
// to change the name of the root element of a serialization hierarchy."
You would add the XmlRootAttribute to a class like this:
using System.Xml.Serialization;
[XmlRootAttribute(Name="Human", IsNullable=false)]
public class Person
{
// class implementation goes here
}
If we make this change in the class declaration of listing 1, and serialize the instance of this class we will get the XML generated resembling the one shown below:
We would be using UTF-8 encoding for the creating the XML stream for the custom object as it supports a wide range of Unicode character values and surrogates. For this purpose, we will make use of the UTF8Encoding class provided by the .Net framework. For more information on UTF8Encoding you may want to refer to MSDN at:
For explaining the concept of XML serialization I will create and use a demo class Animal with a number of properties that describe an animal such as name, length, weight, place of existence etc. First create a C# console application using the Visual Studio.Net and name the project as MySerializer. This will automatically create a namespace MySerializer. Import the following namespaces into the project:
1.
System.Xml.Serialization
2.
System.IO
3.
System.Text
Now add a new class and name it Animal. The code for this class is given below:
using System;
namespace MySerializer
{
[XmlRootAttribute(ElementName="WildAnimal", IsNullable=false)]
public class Animal
{
public Animal() { }
private String animalName;
private String foodTypeCategory;
private Boolean isDomesticed;
private String placeOfExistence;
private Int32 length;
private Int32 height;
private Int32 weight;
public String AnimalName
{ get { return animalName; } set { animalName = value; } }
public String FoodTypeCategory
{ get { return foodTypeCategory; }
set { foodTypeCategory = value; } }
public Boolean IsDomesticed
{ get { return isDomesticed; }
set { isDomesticed = value; } }
public String PlaceOfExistence
{ get { return placeOfExistence; } set { placeOfExistence = value; } }
public Int32 Length { get { return length; } set { length = value; } }
public Int32 Height { get { return height; } set { height = value; } }
public Int32 Weight { get { return weight; } set { weight = value; } }
}
}
Listing 2: Custom class whose objects are to be serialized to XML
When you create the C# console application a default class by the name of Class1 gets added to the project. This class contains the definition of the Main function that is the entry point of the assembly. Rename this class from "Class1" to clsMain. In this class clsMain, we will define the custom methods to serialize and deserialize the object of the class Animal. Apart from these there are helper methods to convert a UTF-8 byte array to string and vice - versa.
Listing 4: Method to serialize the custom business object
Let us look through the code carefully. First we create an instance of the MemoryStream class since we intend to keep the XML string in memory instead of saving it to a file. Instantiate the XmlSerializer class with the type of the class that is to be serialized. In our case this is the class Animal. Now we use the XmlTextWriter Class that provides a fast, non-cached, forward-only way of generating streams or files containing XML data. Notice that we use an overloaded constructor of the XmlTextWriter class and set the encoding to UTF-8. This means that the XML document will be created with UTF-8 encoding.
Now, we go to the most important step and that is calling one of the overloaded implementations of the Serialize method of the XmlSerializer object. We now read the XML stream generated by the serializer from the BaseStream of the XmlTextWriter. Since the BaseStream property returned by the XmlTextWriter is of the type System.IO.Stream, we can easily type cast it into MemoryStream (which is derived from System.IO.Stream) for our purpose.
We now pass the Byte[] array returned by the ToArray() method of the MemoryStream object to the helper function UTF8ByteArrayToString. This method constructs the XML string from the binary array and returns the same. This small piece of trick is performed by the overloaded implementation of the GetString() method of the UTF8Encoding class.
Notice that all the properties defined in the class Animal are serialized into nodes and the name of the root also gets changed from Animal to WildAnimal, this is because we had included the attribute XmlRootAttribute in the declaration of the class Animal.
Deserialization
Just as we had created a custom method to serialize the object, we will create another method in the class clsMain which will use the Deserialize() method of the XmlSerializer class to recreate the object from the XML string.
The code for this method is given below:
public Object DeserializeObject(String pXmlizedString)
{
XmlSerializer xs = new XmlSerializer(typeof(Automobile));
MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(pXmlizedString));
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
return xs.Deserialize(memoryStream);
}
To get the Animal object back from the XML string you will simply call this method and pass the XML string. It will first convert the XML string to a byte array (exactly the reverse of what we did in serialization) and then recreate the object from the XmlSerializer.
There you are!! You have now learnt the entire process of serializing a custom object into an XML string and keeping it in an in memory variable and then reconstructing it back using deserialization. With the help of powerful XMLDocument class and other rich XML parsing classes and readers (such as XmlReader) provided by .Net you can change values or nodes , change attributes and apply advanced XML processing functions to XML document.