'InkPresenter'에 해당되는 글 9건
- 2007/12/14 InkPresenter 방명록 만들기 - 6. Stroke Size Controler User Control 만들기 (4)
- 2007/11/07 InkPresenter 방명록 만들기 - 5. Color Palette User Control 만들기 (2)
- 2007/10/23 InkPresenter 방명록 만들기 - 4. InkPresenter User Control 만들기
- 2007/10/16 shiverlight.net 업데이트
- 2007/10/12 InkPresenter 방명록 만들기 - 3. Undo & Redo 구현하기
- 2007/10/11 InkPresenter 방명록 만들기 - 2. 간단한 샘플 만들어보기 (작동완구)
- 2007/10/09 InkPresenter 방명록 만들기 - 1. InkPresenter 객체 겉핥기
- 2007/10/09 InkPresenter 방명록 만들기 - 0.목차
- 2007/09/26 실버라이트로 만든 방명록
InkPresenter 방명록 만들기 - 6. Stroke Size Controler User Control 만들기

2. InkPresenter를 사용한 간단한 샘플 만들어보기 (작동완구)
3. Undo & Redo 구현하기
4. InkPresenter User Control 만들기
5. Color Palette User Control 만들기
6. Stroke Size Controler User Control 만들기
7. HTTP Request와 Response를 이용하여 정보 교환하기 (Get, Post 전송)
8. Page Navigation User Control 만들기
5주만에 강좌를 재계합니다. 정말 바빴습니다. 믿어주십시오!
그 사이 Silverlight Demo 사이트 쉬버라이트닷넷을 약간 단장하였구요.
저희 회사 휴즈플로우의 웹사이트도 간단하게 만들어서 오픈을 하였습니다.
0. Sample Project
체험장 : http://shiverlight.net/InkPresenterSample/InkPresentationSample_StrokeSize
1. Stroke Size Controler User Control 디자인 (UCStrokeSize.xaml)
진행하기에 앞서 다음 포스트를 읽어보실 것을 권장합니다.
Blend를 통해 User Control 추가하기
위와 같이 디자인이 되었습니다.
이제 XAML 엘리먼트 트리만 보셔도 감이 팍팍 오시죠?
XAML 엘리먼트 트리 맨 아래의 HFSlider _oSizeSlider는 아래와 같은 모습의
녀석으로 Slider 역할을 소화하는 녀석입니다. 자세한 사항은 소스를 참고해 주십시오.
3. Stroke Size Control 비하인드 코드 작성 (UCStrokeSize.xaml.cs)
기본적인 것들은 이전 강좌와 비슷하므로 설명하지 않겠습니다. (소스를 보시면 아실 겁니다. ^^)
여기서는 Slider와 관련된 부분만 설명을 드리도록 하겠습니다.
XAML에 추가한 Slider를 참조하기 위해서 클래스에 다음 변수를 추가합니다.
HFSlider _oSizeSlider;
생성자나 Loaded 함수에 아래와 같은 코드를 추가하여,
실제 객체를 찾아 연결해주고 Slider에 관련된 설정을 해줍니다.
_oSizeSlider = _cvMyself.FindName("_oSizeSlider") as HFSlider;
_oSizeSlider.NumberType = NumberType.Integer;
_oSizeSlider.MinValue = 2;
_oSizeSlider.MaxValue = 40;
_oSizeSlider.ValueChanging += new EventHandler(_oSizeSlider_ValueChanging);
_oSizeSlider.ValueChanged += new EventHandler(_oSizeSlider_ValueChanged);
결과적으로 Slider는 2~40 사이의 범위 안에서 정수를 반환해 주게 됩니다.
이것은 0.5 단위의 Stroke Size를 얻어내기 위해서 사용한 꼼수 입니다. (주석 참조)
이벤트가 2가지 보이는데,
ValueChanging은 사용자가 Slider의 Handle을 잡고 움직이는 동안(MouseMove)에도 이벤트가 발생하고,
ValueChanged는 사용자가 Slider를 움직이다가 손을 때었을 때(MouseLeftButtonUp) 이벤트가 발행합니다.
{
// 스트로크 사이즈를 업데이트
StokeSize = GetStrokeSizeFromSlider();
}
void _oSizeSlider_ValueChanged(object sender, EventArgs e)
{
// 스트로크 사이즈를 업데이트
StokeSize = GetStrokeSizeFromSlider();
// 이벤트 발생
OnSizeChanged();
}
그럼, StokeSize 프로퍼티의 set을 살펴보면 값이 할당되면 동적으로 어떤 일들을 하는지 보실 수 있습니다.
double _dStrokeSize;
public double StokeSize
{
get { return _dStrokeSize; }
set
{
_dStrokeSize = value;
_oSizeSlider.CurrentValue = _dStrokeSize * 2;
// Outline Thickness 고려
_elSize.Width = _dStrokeSize + 4;
_elSize.Height = _dStrokeSize + 4;
_elSize.SetValue(Canvas.LeftProperty, (_cvCurSize.Width - _elSize.Width) / 2);
_elSize.SetValue(Canvas.TopProperty, (_cvCurSize.Height - _elSize.Height) / 2 - 3);
_tbSize.Text = string.Format("{0:F1}", _dStrokeSize);
_tbSize.SetValue(Canvas.LeftProperty, (_cvCurSize.Width - _tbSize.ActualWidth) / 2);
}
}
4. 샘플 프로젝트에 적용
Page.xaml을 Blend에서 Open 합니다.
Asset Library를 통해서 UCStrokeSize User Control을 Page.xaml에 추가합니다.
(만약 모르시는 부분이면 다음 포스트를 먼저 읽으십시오. http://gilverlight.net/2674)
이름을 _oStrokeSize라고 줍니다.
Page.xaml.cs의 생성자에서 다음과 같이 초기화를 해줍니다.
_oStrokeSize.StokeSize = _dInitialStrokeSize;
_oStrokeSize.Color = _oPalette.Color;
_oStrokeSize.SizeChanged += new EventHandler(_oStrokeSize_SizeChanged);
SizeChanged 이벤트를 구현해 줍니다.
{
_oInkEditor.StrokeSize = new Size(_oStrokeSize.StokeSize, _oStrokeSize.StokeSize);
}
팔레트의 색상이 변경될 때마다 Stroke Size Controler에서도 자기가 보여주고 있는
Stroke 미리보기의 색상을 변경해 줍니다.
{
_oInkEditor.StrokeColor = color;
_oStrokeSize.Color = color;
}
이제 Stroke Size Controler 추가가 끝났습니다.
간단하죠?
5. 보너스 팁
<부제 : 그리는 도중 InkPresenter 영역을 벗어났다 들어와도 계속 그려지게 하는 방법>
UCInk.xaml.cs에 간단히 bool 변수 하나를 이용해서 구현합니다.
일단 아래와 같이 변수를 하나 추가해 주십시오.
private bool _bDrawContinue = false;
전에 없던 MouseEnter 이벤트를 추가합니다.
{
(전략...)
// InkPresenter Mouse Event 필수 4종세트
_ink.MouseLeftButtonDown += new MouseEventHandler(ink_MouseLeftButtonDown);
_ink.MouseMove += new MouseEventHandler(ink_MouseMove);
_ink.MouseEnter += new MouseEventHandler(_ink_MouseEnter);
_ink.MouseLeftButtonUp += new MouseEventHandler(ink_MouseLeftButtonUp);
_ink.MouseLeave += new EventHandler(ink_MouseLeave);
}
void _ink_MouseEnter(object sender, MouseEventArgs e)
{
if (_bDrawContinue == true)
{
StartNewStroke(e.GetStylusPoints(_ink));
}
}
_bDrawContinue의 값을 MouseLeftButtonDown에서 true, MouseLeftButtonUp에서 false로 설정합니다.
{
if (Editable == false)
return;
_ink.CaptureMouse();
StartNewStroke(e.GetStylusPoints(_ink));
_bDrawContinue = true;
}
void ink_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
_ink.ReleaseMouseCapture();
_strokeCurrent = null;
_bDrawContinue = false;
}
이렇게 하면 그리는 도중 영역 밖을 나갔다 들어와도 계속해서 그림이 그려집니다.
이것으로 이번 강좌를 마칩니다.
감사합니다.
InkPresenter 방명록 만들기 - 5. Color Palette User Control 만들기

2. InkPresenter를 사용한 간단한 샘플 만들어보기 (작동완구)
3. Undo & Redo 구현하기
4. InkPresenter User Control 만들기
5. Color Palette User Control 만들기
6. Ink Thickness Palette User Control 만들기
7. HTTP Request와 Response를 이용하여 정보 교환하기 (Get, Post 전송)
8. Page Navigation User Control 만들기
0. Sample Project
체험장 : http://shiverlight.net/InkPresenterSample/InkPresentationSample_Palette
1. Palette User Control 디자인 (UCColorPalette.xaml)
Expression Blend에서 원하는 모습으로 User Control을 디자인 합니다.
여러분 이제 오브젝트 트리만 봐도 감이 팍팍 오시죠?
왼쪽 부분을 통해 현재 색상을 표현해주고,
오른쪽 부분은 사용자가 색상을 고를 수 있게 합니다.
팔레트를 보였다 숨겼다하는 토글 방식 때 사용하기 위한 스토리보드를 두 개 정도 만들어 주었습니다.
사용자가 보게 될 각 색상 UI는 _cvColorSet에 직접 해당 색상이 있는 Rectangle 등을 통해서 직관적으로 작업할 수도 있습니다만,
색상이 많아 질수록 동적으로 생성해 주는 것이 더 편하다고 생각되어, 이 강좌에서는
코드로 생성하도록 하겠습니다.
2. Color Palette 비하인드 코드 작성 (UCColorPalette.xaml.cs)
UI 엘리먼트를 컨트롤 하기 위한 변수를 추가합니다.
// UI Elements
// 기본
Canvas _cvMyself;
// 현재 색상
Canvas _cvCurColor;
Rectangle _btnCurColor;
// 컬러 셋
Canvas _cvColorSet;
Rectangle _rcPalette;
// 스토리보드 : 토글
Storyboard _sbToggleShow;
Storyboard _sbToggleHide;
생성자에서 FindName 함수를 이용하여 UI를 찾아서 해당변수에 할당합니다.
{
System.IO.Stream s =
this.GetType().Assembly.GetManifestResourceStream("InkPresentationSample_Palette.UCColorPalette.xaml");
_cvMyself = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
_btnCurColor = _cvMyself.FindName("_btnCurColor") as Rectangle;
_rcPalette = _cvMyself.FindName("_rcPalette") as Rectangle;
_cvCurColor = _cvMyself.FindName("_cvCurColor") as Canvas;
_cvColorSet = _cvMyself.FindName("_cvColorSet") as Canvas;
_sbToggleShow = _cvMyself.FindName("ToggleShow") as Storyboard;
_sbToggleHide = _cvMyself.FindName("ToggleHide") as Storyboard;
}
일단, 위 그림과 같은 것을 목표로 하여, 팔레트에 색상을 추가하기 위한 변수를 추가합니다.
int _nColor = 0;
public int Count { get { return _nColor; } }
// 색상 배치 관련
Size _szMargin = new Size(10,10); // 컬러셋 Rectangle과의 마진
Size _szColor = new Size(20, 20); // 한 개의 색상의 사이즈
double _dSpace = 5; // 한 색상과 다른 색상과의 거리
// 색상 리스트
List<Color> _oColorList = new List<Color>();
// 선택된 색상
Color _color = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
public Color Color { get { return _color; } }
외부로 노출되는 AddColor 메서드를 작성합니다.
색상 값 하나를 받아 _szColor를 사이즈로 갖는 Ellipse를 하나 만든 뒤에,
_cvColorSet 캔버스에 추가시켜 줍니다.
이 때 알맞게, 위치도 결정해 주고, 색상 수 _nCount도 증가시킵니다.
{
// 색상 리스트에 추가
_oColorList.Add(color);
// 원형 색상 UI 추가
Ellipse el = new Ellipse();
el.Width = _szColor.Width;
el.Height = _szColor.Height;
el.Fill = new SolidColorBrush(color);
el.Cursor = Cursors.Hand;
el.SetValue(Canvas.LeftProperty, _szMargin.Width + _nColor * (_szColor.Width + _dSpace));
el.SetValue(Canvas.TopProperty, _szMargin.Height);
_cvColorSet.Children.Add(el);
// 색상 수 증가
_nColor++;
UpdateUI();
}
이때 색상 수가 늘어남에 따라 UI가 변경되어야 하기 때문에 UpdateUI를 작성하여 호출합니다.
{
_rcPalette.Width = _szMargin.Width * 2 + (_szColor.Width + _dSpace) * _nColor - _dSpace;
_cvColorSet.Width = _rcPalette.Width;
}
자, 이제 어떤 색상이 선택되었을 때 발생시킬 이벤트를 준비합니다.
클래스 정의 외부에 적당한 delegate를 하나 준비합니다.
{
public delegate void PaletteEventHandler(Color color);
public class UCColorPalette : Control
{
아래와 같은 프로퍼티를 추가해 줍니다.
public event PaletteEventHandler ColorChanged;
이벤트 하나 작성합니다.
{
if (ColorChanged != null)
{
ColorChanged(_color);
}
}
자 이제 이 함수를 적당한 곳에서 호출해 주기만 하면 이벤트 준비가 끝나겠죠?
어디가 적당할까요? 사용자가 ColorSet 중의 색상을 클릭할 때가 되겠죠!
AddColor 함수로 돌아가 아래와 같이 이벤트에 대한 함수를 작성합니다.
{
// 색상 리스트에 추가
_oColorList.Add(color);
// 원형 색상 UI 추가
Ellipse el = new Ellipse();
el.Width = _szColor.Width;
el.Height = _szColor.Height;
el.Fill = new SolidColorBrush(color);
el.Cursor = Cursors.Hand;
el.MouseLeftButtonUp += new MouseEventHandler(el_MouseLeftButtonUp);
el.SetValue(Canvas.LeftProperty, _szMargin.Width + _nColor * (_szColor.Width + _dSpace));
el.SetValue(Canvas.TopProperty, _szMargin.Height);
_cvColorSet.Children.Add(el);
// 색상 수 증가
_nColor++;
UpdateUI();
}
void el_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
if (sender == null)
return;
Ellipse el = sender as Ellipse;
SolidColorBrush brush = el.Fill as SolidColorBrush;
SetColor(brush.Color);
}
void SetColor(Color color)
{
_color = color;
_btnCurColor.Fill = new SolidColorBrush(_color);
// 이벤트 발생
OnColorChanged();
}
el_MouseLeftButtonUp -> SetColor -> OnColorChanged
결국 이벤트는 위와 같은 경로로 발생하게 되었구요.
User Control 내부에서든, 외부에서든, 컬러가 변경되게 되면 궁극적으로는
SetColor 함수를 호출하도록 신경을 써주시면 됩니다.
이제 아래 그림과 같이 왼쪽 부분을 누르면 ColorSet이 생겼다 없어졌다하는 토글 기능을
구현합니다.
클래스 정의 외부에 enum 타입으로 Style의 Domain을 정의합니다.
항상 보이기와, 토글 두 가지의 스타일을 지원합니다.
{
public enum Style { AlwaysShow, Toggle }
public delegate void PaletteEventHandler(Color color);
public class UCColorPalette : Control
{
프로퍼티를 추가합니다.
Style _Style = Style.Toggle;
bool _bToggleShow = false;
여기서는 동적으로 Style을 변경할 수 있게 지원 안하고 ^^;
생성할 당시에 운명이 정해지도록 하겠습니다.
생성자에 아래와 같이 작업을 합니다.
{
System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("InkPresentationSample_Palette.UCColorPalette.xaml");
_cvMyself = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
_btnCurColor = _cvMyself.FindName("_btnCurColor") as Rectangle;
_rcPalette = _cvMyself.FindName("_rcPalette") as Rectangle;
_cvCurColor = _cvMyself.FindName("_cvCurColor") as Canvas;
_cvColorSet = _cvMyself.FindName("_cvColorSet") as Canvas;
_sbToggleShow = _cvMyself.FindName("ToggleShow") as Storyboard;
_sbToggleHide = _cvMyself.FindName("ToggleHide") as Storyboard;
_Style = style;
// 접혀있는 상태
if (_Style == Style.Toggle)
{
_bToggleShow = false;
_cvColorSet.Opacity = 0;
_btnCurColor.MouseLeftButtonUp += new MouseEventHandler(_btnCurColor_MouseLeftButtonUp);
}
}
void _btnCurColor_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
// Palette 펼쳤다 접었다.
_bToggleShow = !_bToggleShow;
if (_bToggleShow == true)
{
ShowPalette();
}
else
{
HidePalette();
}
}
public void ShowPalette()
{
_sbToggleShow.Begin();
_cvColorSet.IsHitTestVisible = true;
_bToggleShow = true;
}
public void HidePalette()
{
_sbToggleHide.Begin();
_cvColorSet.IsHitTestVisible = false;
_bToggleShow = false;
}
사용자가 ColorSet에서 색상을 골랐을 때도 Hide가 되게 합니다.
{
if (sender == null)
return;
Ellipse el = sender as Ellipse;
SolidColorBrush brush = el.Fill as SolidColorBrush;
SetColor(brush.Color);
HidePalette();
// 이벤트 발생
OnColorChanged();
}
자 이제 마지막으로 이 컨트롤을 가지고 작업할 프로그래머 편의를 위한 함수를
2개 정도만 추가하고 마무리합니다.
이 Palette가 현재 어떤 어떤 색상을 가지고 있는지 알 수 없기 때문에,
Index를 통해서 색상을 지정해 줄 수 있는 SelectColor(int nIndex) 함수와,
User Control의 위치를 손쉽게 변경할 수 있게 Position(Point pt) 함수를
서비스로 작성해 줍니다.
{
if (nIndex < 0 || nIndex >= _oColorList.Count)
return;
SetColor(_oColorList[nIndex]);
}
public void Position(Point pt)
{
SetValue(Canvas.LeftProperty, pt.X);
SetValue(Canvas.TopProperty, pt.Y);
}
3. 샘플 프로젝트에 적용
Page에서 Palette를 사용하기 위해 프로퍼티를 추가합니다.
Page.xaml.cs에서
Page_Loaded 함수 안에서 Palette를 생성합니다.
CreateInkEditor보다 먼저 생성해야 합니다.
이유는 곧 아시게 됩니다.
{
....
// 배경이미지 준비
LoadBackImages();
// Palette 생성
CreatePalette();
// Editor 생성
CreateInkEditor();
...
}
void CreatePalette()
{
_oPalette = new UCColorPalette(Style.Toggle);
_oPalette.Position(new Point(405, 50));
Children.Add(_oPalette);
// 색상 추가 : 닥치는 대로
_oPalette.AddColor(Color.FromRgb(0xC1, 0x31, 0x31));
_oPalette.AddColor(Color.FromRgb(0xE3, 0x62, 0x1D));
_oPalette.AddColor(Color.FromRgb(0xE3, 0xCF, 0x1D));
_oPalette.AddColor(Color.FromRgb(0xB7, 0xE3, 0x1D));
_oPalette.AddColor(Color.FromRgb(0x1D, 0xE3, 0x68));
_oPalette.AddColor(Color.FromRgb(0x1D, 0xE3, 0xA7));
_oPalette.AddColor(Color.FromRgb(0x1D, 0xD5, 0xE3));
_oPalette.AddColor(Color.FromRgb(0x1D, 0x7A, 0xE3));
_oPalette.AddColor(Color.FromRgb(0x80, 0x1D, 0xBD));
_oPalette.AddColor(Color.FromRgb(0xFF, 0xFF, 0xFF));
_oPalette.AddColor(Color.FromRgb(0xCC, 0xCC, 0xCC));
_oPalette.AddColor(Color.FromRgb(0x99, 0x99, 0x99));
_oPalette.AddColor(Color.FromRgb(0x7F, 0x7F, 0x7F));
_oPalette.AddColor(Color.FromRgb(0x66, 0x66, 0x66));
_oPalette.AddColor(Color.FromRgb(0x4D, 0x4D, 0x4D));
_oPalette.AddColor(Color.FromRgb(0x33, 0x33, 0x33));
_oPalette.AddColor(Color.FromRgb(0x1C, 0x1C, 0x1C));
_oPalette.AddColor(Color.FromRgb(0x00, 0x00, 0x00));
_oPalette.SelectColor(9); // White
_oPalette.ColorChanged += new PaletteEventHandler(_oPalette_ColorChanged);
}
여기서 드디어 ColorChanged 이벤트 핸들러를 사용합니다.
_oPalette_ColorChanged 함수를 구현하기에 앞서 UCInk.xaml.cs를 방문합니다.
UCInk.xaml.cs에서 외부에서 색상을 입력 받을 프로퍼티를 추가합니다.
그리고 새 Stroke를 생성할 때 마다 그 색상을 가져다 쓰도록 합니다.
{
// 스트로크 준비 흰색
Stroke stroke = new Stroke();
stroke.DrawingAttributes.Color = StrokeColor;
stroke.DrawingAttributes.OutlineColor = Colors.Black;
stroke.DrawingAttributes.Width = 2;
stroke.DrawingAttributes.Height = 2;
return stroke;
}
다시 Page.xaml.cs로 돌아와 못다한 일을 마칩니다.
{
_oInkEditor.StrokeColor = color;
}
마지막으로 새 작품을 만들 때, UCInk의 Stroke가 팔레트에 현재 색상을
가져다 쓸 수 있도록 CreateInkEditor 함수를 손봐줍니다.
{
// 새 InkPresenter Editor 생성
_oInkEditor = new UCInk(_szInk, true);
_oInkEditor.BackImage = GetImageRandom();
if (_oPalette != null)
_oInkEditor.StrokeColor = _oPalette.Color;
// Canvas에 추가
cvEditor.Children.Add(_oInkEditor);
}
여기서 _oPalette를 사용하기 때문에 CreateInkEditor보다 CreatePalette를 먼저 호출해 주셔야 한다고
그랬던 겁니다.
자, 끝났습니다.
별거 아닌데 말이 너무 길었습니다. ^^;;;
감사합니다.
InkPresenter 방명록 만들기 - 4. InkPresenter User Control 만들기

2. InkPresenter를 사용한 간단한 샘플 만들어보기 (작동완구)
3. Undo & Redo 구현하기
4. InkPresenter User Control 만들기
5. Color Palette User Control 만들기
6. Ink Thickness Palette User Control 만들기
7. HTTP Request와 Response를 이용하여 정보 교환하기 (Get, Post 전송)
8. Page Navigation User Control 만들기
안녕하세요 오랜만입니다.
0. Sample Project
샘플 프로젝트 먼저 보는 것으로 4부를 시작하겠습니다.
백문이 불여일견. 코드 아무리 보면 뭐합니다. 일단 만져보는게 백번 낫죠?
체험장 : http://www.shiverlight.net/InkPresenterSample/InkPresentationSample_UserControl/
일단 배경 이미지 다섯 개를 준비했습니다.
랜덤하게 배경을 깔아주고 그 위에 Ink로 쓰실 수 있습니다.
Save 버튼 위에 있는 버튼을 누르면 배경을 바꿀 수 있습니다. (랜덤입니다.)
Save 버튼을 누르면 바로 전까지 그림 그리면 가지고 놀던 그 UserControl이
아래 List에 추가됩니다. 그리고 새 UserControl이 준비됩니다.
이것은 어디까지나 샘플입니다 DB같은데 저장되는 것은 아닙니다.
자세한 설명은 생략하겠습니다.
첨부한 프로젝트의 소스코드를 보시면 다 이해하실 것이라 믿습니다.
PS. 첨부한 프로젝트는 이전 강좌의 누적판으로 준비했습니다.
1. User Control 구상 (UCInk.xaml)
User Control을 만들어 사용할 때의 이점은 모두 다 아시리라 믿고 본론 들어갑니다.
일단 아래와 같은 스펙을 구상해 볼 수 있겠습니다.
하나 더, 외부에서 사이즈를 줘서 마음먹은 크기로 만들 수 있게 하면 좋겠죠.
2. UCInk.xaml 생성하고 프로퍼티 준비
새 실버라이트 프로젝트를 생성한 후,
VS2008의 솔루션 탐색기에서 프로젝트 이름에 오른쪽 클릭하고 추가 - 새 항목을 누릅니다.
새 항목 추가 대화상자의 템플릿 중, Silverlight User Control을 선택하고,
이름을 UCInk.xaml 이라 합니다.
UCInk.xaml 파일에 아래와 같이 이름이 "ink"인 InkPresenter 객체를 추가합니다.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="White"
>
<InkPresenter x:Name="ink" Background="White"/>
</Canvas>
그리고 UCInk.xaml의 코드 비하인드인 UCInk.xaml.cs 파일을 열어,
User Control 내부의 객체를 컨트롤 하기 위 UCInk 클래스에 아래 프로퍼티를 추가합니다.
Canvas _cvMyself;
생성자를 아래와 같이 변경합니다.
{
System.IO.Stream s =
this.GetType().Assembly.GetManifestResourceStream("InkPresentationSample_UserControl.UCInk.xaml");
_cvMyself = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
_ink = _cvMyself.FindName("ink") as InkPresenter;
}
이제 여러분은 _ink를 통해서 InkPresenter 객체를 콘트롤 할 수 있게 된 것입니다.
InkPresenter의 배경이미지와 편집 가능여부를 자유롭게 변경하기 위해 아래 프로퍼티를 또 추가합니다.
Uri _BackImage = null;
public Uri BackImage { get { return _BackImage; } set { SetBackgroundImage(value); } }
기본 생성자를 아래와 같이 변경하고,
SetBackgroundImage, UpdateSize 함수를 추가합니다.
{
System.IO.Stream s =
this.GetType().Assembly.GetManifestResourceStream("InkPresentationSample_UserControl.UCInk.xaml");
_cvMyself = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd()) as Canvas;
_ink = _cvMyself.FindName("ink") as InkPresenter;
// 사이즈 설정
UpdateSize(sz);
// 편집 가능 여부
Editable = bEditable;
}
void SetBackgroundImage(Uri uri)
{
if (uri == null)
return;
_BackImage = uri;
ImageBrush ib = new ImageBrush();
ib.ImageSource = _BackImage;
_ink.Background = ib;
}
void UpdateSize(Size sz)
{
// Control 사이즈
Width = sz.Width;
Height = sz.Height;
// 무시해도 좋지만 해주면 좋음
_cvMyself.Width = Width;
_cvMyself.Height = Height;
// InkPresenter 사이즈
_ink.Width = Width;
_ink.Height = Height;
}
Ink 쓰기를 가능하게 하기 위해, 3부에서 많이 익숙해지신 아래 녀석들을 클래스 프로퍼티로 추가합니다.
Stroke _strokeCurrent = null;
StrokeCollection _oUndoBuffer = new StrokeCollection();
3. Ink 쓰기 구현
3부에서 했던 것이라 쉽습니다.
생성자에 Ink 쓰기 이벤트 4종 세트를 추가합니다.
_ink.MouseLeftButtonDown += new MouseEventHandler(ink_MouseLeftButtonDown);
_ink.MouseMove += new MouseEventHandler(ink_MouseMove);
_ink.MouseLeftButtonUp += new MouseEventHandler(ink_MouseLeftButtonUp);
_ink.MouseLeave += new EventHandler(ink_MouseLeave);
각각 함수는 아래와 같이 구현됩니다. 3부에서 보신 함수와 거의 같습니다만,
편집 가능/불가능 여부를 지원하기 위해 ink_MouseLeftButtonDown함수에서
변수 Editable을 체크하고 있습니다.
{
if (Editable == false)
return;
_ink.CaptureMouse();
StartNewStroke();
}
void ink_MouseMove(object sender, MouseEventArgs e)
{
if (_strokeCurrent == null)
return;
_strokeCurrent.StylusPoints.AddStylusPoints(e.GetStylusPoints(_ink));
}
void ink_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
_ink.ReleaseMouseCapture();
_strokeCurrent = null;
}
void ink_MouseLeave(object sender, EventArgs e)
{
_strokeCurrent = null;
}
아래 AllocStroke, StartNewStroke, Flush_oUndoBuffer 함수에 대한 내용은 3부에서
다뤘으므로 설명을 생략하도록 하겠습니다.
{
// 스트로크 준비 흰색
Stroke stroke = new Stroke();
stroke.DrawingAttributes.Color = Colors.White;
stroke.DrawingAttributes.OutlineColor = Colors.Black;
stroke.DrawingAttributes.Width = 2;
stroke.DrawingAttributes.Height = 2;
return stroke;
}
void StartNewStroke()
{
_strokeCurrent = AllocStroke();
_ink.Strokes.Add(_strokeCurrent);
// Undo, Redo 버퍼 초기화
Flush_oUndoBuffer();
}
void Flush_oUndoBuffer()
{
_oUndoBuffer.Clear();
}
4. public Method 구현 (Undo, Redo, Clear)
{
if (_ink.Strokes.Count == 0)
return;
Stroke oUndo = _ink.Strokes[_ink.Strokes.Count - 1];
_oUndoBuffer.Add(oUndo);
_ink.Strokes.RemoveAt(_ink.Strokes.Count - 1);
}
public void Redo()
{
if (_oUndoBuffer.Count == 0)
return;
Stroke oRedo = _oUndoBuffer[_oUndoBuffer.Count - 1];
_ink.Strokes.Add(oRedo);
_oUndoBuffer.RemoveAt(_oUndoBuffer.Count - 1);
}
public void Clear(bool bClearBackImage)
{
_ink.Strokes.Clear();
if (bClearBackImage == true)
{
_ink.Background = null;
}
Flush_oUndoBuffer();
}
Undo, Redo는 3부에서 다룬 내용과 다르지 않습니다.
Clear에 보시면, 배경이미지를 함께 지울 것인지 파라미터로 받고 있습니다.
감사합니다.
그럼 다음 5부에서 또 뵙겠습니다.
shiverlight.net 메인 페이지가 업데이트 되었습니다.
맨 앞에부터 최근작입니다. 앞에서부터 간략한 소개
Portrait Movie!
실버라이트에서 손쉽게 동영상을 회전하여 보여줄 수 있으므로,
앞으로는 '캠코더를 이용하여 세로로 동영상을 촬영해도 됩니다.'라는 내용을 담고 있습니다.
- by Gilbert
Shiver Mario
Farseer Physics Engine을 활용하여 간단하게 Engine의 활용을 보여주고 있습니다.
- by Gilbert
3D Cube (Image Brush)
Skewed Rectangle이 아닌 Path 객체를 사용하여 원근감이 적용된 3D Cube에
Image Brush를 사용한 작품입니다.
- by boxmile
RSS Reader
블로그 RSS Feed를 읽어 표현해 주는 샘플입니다.
맑은 고딕체 폰트를 Client에 자동 다운로드가 되게하여, 한글을 표시하고 있습니다.
- by Gilbert
Guest Ink
InkPresenter 객체를 사용하여 구현한 방명록입니다.
화면의 크기에 따라 한 페이지에 보여지는 방명록의 개수가 동적으로 변경됩니다.
HttpRequest와 HttpResponse를 이용하여 DB에 데이터를 저장하고 있습니다.
- by Gilbert
3D Cube
Skewed Rectangle이 아닌 Path 객체를 사용하여 원근감이 적용된 3D Cube
- by boxmile
Simple Clock
Storyboard를 이용한 Timer를 활용하여 시간을 업데이트 해주는 간단한 시계입니다.
- by Gilbert
Excellon Drill Viewer (beta)
전자제품 CAM Data의 일부인 Drill Data를 웹상에서 보여줄 수 있는 샘플입니다.
CAM Application을 Silverlight로도 제작할 수 있다는 가능성을 실험한 작품입니다.
- by Gilbert
Spin World
마우스 휠버튼을 이용하여 동영상과 이미지를 동등한 레벨로 네비게이트합니다.
- boxmile
Shiver Chat
채팅방을 흉내낸 작품. 상설 채팅방입니다.
- Gilbert
Cat Chasing
고양이가 물고기를 먹지 못하게 해야 점수가 올라가는,
고양이 약올리기 게임입니다.
Microsoft Silverlight 공식 사이트 갤러리에서도 보실 수 있습니다.
-boxmile
InkPresenter 방명록 만들기 - 3. Undo & Redo 구현하기

2. InkPresenter를 사용한 간단한 샘플 만들어보기 (작동완구)
3. Undo & Redo 구현하기
4. InkPresenter User Control 만들기
5. Color Palette User Control 만들기
6. Ink Thickness Palette User Control 만들기
7. HTTP Request와 Response를 이용하여 정보 교환하기 (Get, Post 전송)
8. Page Navigation User Control 만들기
안녕하세요. 3부입니다.
불가*스가 1,200원으로 올라 아침부터 기분이 약간 상했습니다.
상한 기분을 강좌로 차분하게 달래 볼까합니다. ^^
(첨부된 파일은 2부 강좌의 누적 소스입니다.)
1. Page xaml에 Undo, Redo 버튼을 만들어 줍니다.
저같은 경우는 아래 그림같이 하고 넘어갔습니다.
하는 김에 Clean 버튼도 만들어 줍니다.
오른쪽의 밝은 색 버튼 보이시죠? 스토리보드 같은 사치스러운 것은 생략!
XAML 트리에서 보시면
아래쪽에 cvUndo와 cvRedo라는
Canvas가 새로 추가되었습니다.
C# 코드로 이벤트를 할당하기 위해
손수 이름을 지어주었구요.
Rectangle 위에 Text를 위치하고
Canvas로 싼 다음에 Canvas에 이벤트를 주는
이유는 Rectangle에 이벤트를 주게되면
Text 위에 마우스를 올렸을 때
Rectangle의 Mouse Event가
일어나지 않기 때문입니다.
2. Undo, Redo Buffer 추가
Undo, Redo는 이거 하나면 끝입니다.
Page.xaml.cs 파일의 Page 클래스에 멤버 변수로 아래 것을 추가합니다.
StrokeCollection UndoBuffer = new StrokeCollection();
3. Undo, Redo, Clear 버튼 이벤트
Page.xaml.cs 파일의 Page_Loaded 함수에 아래를 추가하고
cvUndo.MouseLeftButtonUp += new MouseEventHandler(cvUndo_MouseLeftButtonUp);
cvRedo.MouseLeftButtonUp += new MouseEventHandler(cvRedo_MouseLeftButtonUp);
// Clear Event
cvClear.MouseLeftButtonUp += new MouseEventHandler(cvClear_MouseLeftButtonUp);
각각의 함수를 아래와 같이 구현합니다.
void cvUndo_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
if (ink.Strokes.Count == 0)
return;
Stroke oUndo = ink.Strokes[ink.Strokes.Count - 1];
UndoBuffer.Add(oUndo);
ink.Strokes.RemoveAt(ink.Strokes.Count - 1);
}
// Redo
void cvRedo_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
if (UndoBuffer.Count == 0)
return;
Stroke oRedo = UndoBuffer[UndoBuffer.Count - 1];
ink.Strokes.Add(oRedo);
UndoBuffer.RemoveAt(UndoBuffer.Count - 1);
}
// Clear
void cvClear_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
ink.Strokes.Clear();
}
4. FlushBuffer 함수
사용자가 새로 획을 그을 때마다 UndoBuffer를 비워(Flush)주지 않으면,
원치 않는 결과를 초래할 수 있습니다. (간혹 원하는 사람도 있겠죠...)
예를 들어 [1]획, [2]획, [3]획까지 그리고 Undo를 누르면,
화면에는 [1], [2] 획이 표시되고, UndoBuffer에는 [3]획이 존재하게 됩니다.
여기서 사용자가 [4]획을 그은 후에, Redo 버튼을 누르면, 화면에는
[1], [2], [4], [3]이 표시되게 됩니다.
이런 결과를 원치 않으시는 분만 아래를 보시면 되겠습니다.
간단하게 FlushBuffer를 구현합니다.
{
UndoBuffer.Clear();
}
이제 이걸 언제, 어디서 불러줘야 할까요?
맞습니다. 새로 획을 그을 때마다 불러줘야 합니다.
2부에서 구현했던 StartNewStroke() 함수와
조금 전 구현한 Clear 버튼 MouseLeftButtonUp 이벤트 함수에 살포시 끼워 넣습니다.
{
_strokeCurrent = AllocStroke();
ink.Strokes.Add(_strokeCurrent);
// Undo, Redo 버퍼 초기화
FlushUndoBuffer();
}
...
// Clear
void cvClear_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
ink.Strokes.Clear();
// Undo, Redo 버퍼 초기화
FlushUndoBuffer();
}
3부는 이것으로 마칩니다.
샘플을 작동해보실 수 있는 모델하우스 :
http://www.shiverlight.net/InkPresenterSample/InkPresenterSample_UndoRedo
4부도 기대해 주세요.
감사합니다. 꾸벅!
InkPresenterSample6.zip
이올린에 북마크하기
이올린에 추천하기


