TextBox 에 워터마크를 삽입하는 것은 이 전의 글에서 이야기했었고, 

동일한 방법으로 스타일을 만들어 Key와 TargetType 만 변경하여 PasswordBox에 적용하려 했으나 에러가 발생했다. 


이유는 PasswordBox 에는 'Text' 속성이 없고 이 대신 'Password' 속성이 있지만 이 속성은 의존속성(Dependency Property) 가 아니기 때문에 트리거로 사용할 수 없다. 


다시 TextBox의 예로 돌아와서 Trigger 부분을 살펴보자. 


4번째 줄에 보면 'Text' 속성이 "" 일 때에 대한 조건이 있는데 

해당 TextBox가 focus 되지 않고, 또한 내용이 입력되지 않았을 때 워터마크를 보여주도록 지정한다.


하지만 PasswordBox 에서는 이 'Text' 속성이 없고, 'Password' 속성이 의존속성이 아니기 때문에 다음의 사이트를 참조하여 구현한다. 


http://stackoverflow.com/questions/1607066/wpf-watermark-passwordbox-from-watermark-textbox



방법은 PasswordBoxMonitor 라는 의존객체를 만들어 Password 속성을 모니터링하고, 변경되었을 시 자체적으로 만든 'PasswordLength' 라는 속성을 통해 해당 PasswordBox 에 내용이 있는지, 혹은 없는지를 판단하도록 한다.




입력창, 특히 로그인 창에 회색으로 'ID를 입력하세요' 내지 'example@example.com ' 처럼 예시를 통해

사용자의 입력을 돕는 기능을 흔히 볼 수 있다.


아래는 네이버 로그인 창인데, '아이디' , '비밀번호' 부분을 보면 focus 가 되면 글씨가 사라지고 입력이 없고 focus되지 않으면 나타나 입력을 돕는다.




웹에서는 <input> 태그에서 'placeholder' 속성으로 구현을 하는데, WPF 의 Textbox 에는 이 기능이 없기 때문에 직접 구현해야 한다. 



1. Resource 를 통해 watermark 를 구현

2. 사용하고자 하는 TextBox에 1에서 정의한 Style 을 적용



1.

TextBox 에 스타일을 변경하여 watermark 를 삽입할 'Label' 하나를 추가하는 방법으로 구현한다.

해당 Label 은 Focus되거나 Textbox에 값이 있을 때는 보이지 않도록 Trigger 를 통해 조절한다.


해당 스타일은 별도의 ResourceDictionary 파일로 분리하여 사용했으나 기존에 사용하고 있던 Resource 파일이 있다면 거기에 추가하거나, 혹은 현재 파일 내부에 삽입해도 된다.


코드는 다음과 같으며, stackoverflow 의 게시물 을 참조하였다.






중간에 <Grid> 내부에서 Watermark 를 위한 Label 을 선언해주고, 

아래부분의 MultiTrigger 에서 각각 Focus되지 않았을 때(isFocused 부분) 와 내부에 Text가 없을 때(Text 부분) 를 정의해준다. 


Watermark 로 보여주고자 하는 내용은 해당 Textbox 의 'Tag' 속성과 바인딩 시켰다.


2. 

<TextBox> 에 Style="{StaticResource WatermarkedTextBox}"Tag="아이디" 속성 두 개를 추가




여기까지 제대로 적용했다면 다음과 같이 동작하는 것을 확인할 수 있다.




- IsDeferredScrollingEnabled 속성

 : ScrollChanged 이벤트가 scroll이 안정화 되었을 때(즉 ScrollViewer가 정지했을 때) 만 발생하도록 한다.


이 속성의 기본값은 'False' 로 되어 있고, 이를 'True'로 변경하면 위와 같이 동작하게 된다.


프로젝트 중에 ScrollViewer 내부에 수많은 이미지를 보여주도록 하는 경우가 있었는데, 메모리 상의 문제로 화면에 보여지는 영역의 이미지를 로드하고, ScrollChanged 이벤트가 발생할 경우 화면에 보여지는 영역을 다시 계산해서 해당 영역의 이미지를 로드하도록 구현하였다. 이렇게 했더니 스크롤을 이동할 때마다 ScrollChanged 이벤트가 많이 발생해서 퍼포먼스 상의 문제가 있어, IsDeferredScrollingEnabled 속성을 이용해 퍼포먼스를 높인 경험이 있었다.





Adorner
 는 UIElement 위에 정보, 혹은 기능을 표시하기 위해 사용하는 사용자 지정 FrameworkElement 이다.


일반적인 용도

- UIElement 에서 사용자가 크기 조정, 회전, 위치 변경과 같은 기능 핸들 추가

- 다양한 상태를 나타내거나 이벤트에 응답하기 위한 시각적 피드백 제공

- 그 외의 다양한 시각적 효과 overlay

등이 되겠다.


Adorner 는 UIElement 내에 AdornerLayer 라는 레이어를 생성하여 그 위에 Adorner 객체를 위치시키므로 해당 UIElement 내부의 어떤 객체들보다 상위에 나타난다. 따라서 항상 위에 나타나야 하는 객체들 - 상태 정보, 기능 핸들 등은 Adorner 를 사용하는 것이 Z-index 등을 조절하는 것보다 편한 방법이 될 것이다.




Adorner 클래스를 사용하는 방법은 다음과 같다.


1. Adorner 클래스를 상속받아 새로운 클래스를 만든다.

  - Adorner 클래스는 추상 클래스(Abstract Class) 이므로 직접 사용할 수 없다.

  - Rendering 을 구현해야 하는데, 일반적으로 OnRender() 메서드를 오버라이드(override)해서 사용한다.

2. Adorner 를 적용하고자 하는 엘리먼트에 AdornerLayer 를 가져온다.

3. 1에서 생성한 클래스를 2에서 가져온 AdornerLayer 에 추가한다.


  AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);

  adornerLayer.Add(myAdorner);


그 외 

- Adorner 는 다른 객체들의 상위에 위치하므로, 마우스 이벤트를 받는다. Adorner 가 마우스 이벤트를 받지 않고 통과시키도록 하려면 IsHitTestVisible 속성을 false로 하면 된다.




MSDN 예시 코드

http://msdn.microsoft.com/ko-kr/library/ms743737.aspx

WPF로 Clipboard 를 사용하여 복사/붙여넣기를 하는 것은 생각보다 매우 간단하다.


System.Windows.Clipboard 클래스 를 사용한다.


1) 복사하기 :

Clipboard.SetText (string text) , Clipboard.SetImage (BitmapSource image) 함수 사용

( 이외에도 SetAudio, SetData, SetDataObject 함수도 존재함)


BitmapImage img = new BitmapImage();

Clipboard.SetImage(img);



2) 붙여넣기 :

Clipboard.GetText(), Clipboard.GetImage(), Clipboard.GetData(), Clipboard.GetDataObject() 등의 함수 사용



BitmapSource source = Clipboard.GetImage();

JpegBitmapEncoder encoder = new JpegBitmapEncoder();

MemoryStream memoryStream = new MemoryStream();


BitmapImage img = new BitmapImage();


encoder.Frames.Add(BitmapFrame.Create(source));

encoder.Save(memoryStream);


img.BeginInit();

img.StreamSource = new MemoryStream(memoryStream.ToArray());

img.EndInit();


memoryStream.Close();


* 사용은 다 비슷비슷하므로 Image 를 예시로 작성해 보았음

이미지를 불러올 경우

가장 간단한 코드는 다음과 같다.


1)

BitmapImage img = new BitmapImage(new Uri(myUri));

Image.Source = img;



검색하다보니 이 코드를 포함해서 여러 개의 코드 조각들 간의

이미지 로딩 속도를 비교한 결과가 있는 블로그가 있었다.

(http://lovehana.com/flyingmt/566?category=8)


위의 블로그에서 가장 속도가 좋다고 나온 코드 조각은 다음과 같다.


2)

BitmapImage img = new BitmapImage();

img.BeginInit();

img.CacheOption = BitmapCacheOption.OnDemand;

img.CreateOptions = BitmapCreateOptions.DelayCreation;

img.DecodePixelWidth = 300;

img.UriSource = new Uri(myUri);

img.EndInit();

Image.Source = img;


단, 2) 번 코드의 문제는 web 상의 이미지는 로드할 수 없다(http:// ... 주소로 된 이미지)





위의 1), 2) 번 코드는 프로세스가 이미지를 사용하고 있어

해당 프로세스나 다른 프로세스에서 해당 이미지를 변경할 수 없다.

즉 이미지를 로드하여 이를 편집하고 덮어쓰기 를 하거나 하는 경우에는 사용할 수 없다.


따라서 이런 경우에는 다음과 같은 코드를 사용한다.


3)

Byte[] buffer;


if (myUri.Substring(0, 4).Equals("http"))

{

    WebClient wc = new WebClient();

    buffer = wc.DownloadData(new Uri(myUri, UriKind.Absolute));

    wc.Dispose();

}

else

{

    buffer = System.IO.File.ReadAllBytes(myUri);

}


MemoryStream ms = new MemoryStream(buffer);

             

BitmapImage img = new BitmapImage();

img.BeginInit();

img.CacheOption = BitmapCacheOption.OnLoad;

img.StreamSource = ms;

img.EndInit();

Image.Source =  img ;


스크롤뷰어를 원하는 지점으로 스크롤시키고자 할 때는

ScrollViewer.ScrollToHorizontalOffset(), ScrollToVerticalOffset() 함수를 사용한다.


원하는 지점까지 스르륵 애니메이션을 적용해서 스크롤시키고 싶은데

위의 함수는 그런 기능을 지원하지 않고,

그렇다면 DoubleAnimation 등의 애니메이션을 사용해야 하는데,


ScrollViewer.HorizontalOffset, ScrollViewer.VerticalOffset 2개의 의존속성은 get 만 지원해서 DoubleAnimation 을 적용할 수 없다.

따라서 스크롤뷰어에서 스크롤 기능에 애니메이션을 적용하려면 스크롤뷰어를 상속받아 별도의 스크롤뷰어를 만드는 방법밖에 없다.


별도의 스크롤뷰어를 만들어 HorizontalOffset, VerticalOffset과 비슷하되 get, set 모두를 지원하는 의존속성을 만들어 이 속성에 애니메이션을 적용하는 방법이다.



과정은 다음과 같다.


1. 새로운 클래스를 선언하고, ScrollViewer 를 상속받도록 선언한다.

  - 예시에서는 'AniScrollViewer' 라고 명명함

2. HorizontalOffset, VerticalOffset 을 대신할 새로운 의존속성을 만든다.

  - 예시에서는 'CurrentVerticalOffset', 'CurrentHorizontalOffset' 라고 명명함

3. 새로 만든 의존 속성이 변경되면 해당 컨트롤의 스크롤이 이동하도록 한다.



4. 기존의 ScrollViewer 를 위에서 만든 새로운 ScrollViewer로 대체하고, 2번에서 만든 의존속성을 사용하여 애니메이션을 적용한다.

  - Animation 객체를 만들고, 해당 애니메이션의 TargetProperty 를 위에서 만든 의존 속성으로 지정한다.

 


다음과 같은 방법으로 스크롤뷰어에 애니메이션을 적용할 수 있다.




Visual Studio 2010을 사용하는 중에, 특정 프로젝트에서 Custom Control(WPF) 를 추가하려고 했으나 리스트에 해당 item 이 나타나지 않았다.


다른 프로젝트에서 아이템을 추가(프로젝트 > 우클릭 - 추가 > 새로운 아이템 > Visual C# items > WPF ) 하면 Window, Page, User Control, Resource Dictionary 등의 8개의 메뉴가 나오는데 해당 프로젝트에서는 아래처럼 User Control 하나만 나왔다.



이유가 혹시 참조를 안했나 해서 확인도 다 해보고, 프로젝트 속성도 검토해보았으나 이유를 찾지 못했는데, 그 해결책은 매우 간단하다.


해당 프로젝트 파일(*.csproj 파일) 을 메모장으로 열어 첫번째 <PropertyGroup> 밑에 다음의 코드를 삽입한다.


<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>


이 부분을 추가하고 다시 프로젝트를 실행시켜 새로운 아이템 추가를 해보았다.


 


다른 프로젝트처럼 8개의 리스트가 모두 추가된 것을 확인할 수 있다.


프로젝트 속성과 관련한 버그인 것으로 보이는데 정확한 이유는 모르겠다.



+ Recent posts