1. InkPresenter 객체 겉핥기 (InkPresenter XAML 구조)
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에 관련된 설정을 해줍니다.

// 슬라이드를 2~40까지의 정수 값을 가지게 하고, Current Value를 1/2 하여 Stroke 사이즈로 사용한다.
_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) 이벤트가 발행합니다.

void _oSizeSlider_ValueChanging(object sender, EventArgs e)
{
    // 스트로크 사이즈를 업데이트
    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의 생성자에서 다음과 같이 초기화를 해줍니다.

// Stroke Size Controler 설정
_oStrokeSize.StokeSize = _dInitialStrokeSize;
_oStrokeSize.Color = _oPalette.Color;
_oStrokeSize.SizeChanged += new EventHandler(_oStrokeSize_SizeChanged);

SizeChanged 이벤트를 구현해 줍니다.

void _oStrokeSize_SizeChanged(object sender, EventArgs e)
{
    _oInkEditor.StrokeSize = new Size(_oStrokeSize.StokeSize, _oStrokeSize.StokeSize);
}

팔레트의 색상이 변경될 때마다 Stroke Size Controler에서도 자기가 보여주고 있는
Stroke 미리보기의 색상을 변경해 줍니다.

void _oPalette_ColorChanged(Color color)
{
    _oInkEditor.StrokeColor = color;
    _oStrokeSize.Color = color;
}

이제 Stroke Size Controler 추가가 끝났습니다.

사용자 삽입 이미지

간단하죠?


5. 보너스 팁

<부제 : 그리는 도중 InkPresenter 영역을 벗어났다 들어와도 계속 그려지게 하는 방법>

UCInk.xaml.cs에 간단히 bool 변수 하나를 이용해서 구현합니다.

일단 아래와 같이 변수를 하나 추가해 주십시오.

// 영역 밖에 나갔다 들어와도 그려지게 해줌
private bool _bDrawContinue = false;

전에 없던 MouseEnter 이벤트를 추가합니다.

public UCInk(Size sz, bool bEditable)
{
    (전략...)

    // 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로 설정합니다.

void ink_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
    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;
}

이렇게 하면 그리는 도중 영역 밖을 나갔다 들어와도 계속해서 그림이 그려집니다.

이것으로 이번 강좌를 마칩니다.

감사합니다.




 

이올린에 북마크하기(0) 이올린에 추천하기(0)

Tag : InkPresenter, RIA, Silverlight, Stroke, 방명록만들기, 실버라이트, , 휴즈플로우

  1. 구피

    정말 잘만드셨네요

    디자인도 너무 깔끔해...ㅠㅠ요

    | 2008/02/27 12:02 | PERMALINK | EDIT | REPLY |
  2. BlogIcon 길버트

    안녕하세요! 길버트 이길복입니다.
    그렇게 말씀해 주시니 감사합니다!
    강좌를 아직 마무리 짓지 않아서 죄송할 따름입니다.

    | 2008/02/28 00:11 | PERMALINK | EDIT | REPLY |
코멘트를 남겨 주세요. (Write your message and submit)
1. InkPresenter 객체 겉핥기 (InkPresenter XAML 구조)
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를 찾아서 해당변수에 할당합니다.

public UCColorPalette()
{
    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도 증가시킵니다.

public void AddColor(Color color)
{
    // 색상 리스트에 추가
    _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를 작성하여 호출합니다.

void UpdateUI()
{
    _rcPalette.Width = _szMargin.Width * 2 + (_szColor.Width + _dSpace) * _nColor - _dSpace;
    _cvColorSet.Width = _rcPalette.Width;
}

자, 이제 어떤 색상이 선택되었을 때 발생시킬 이벤트를 준비합니다.

클래스 정의 외부에 적당한 delegate를 하나 준비합니다.

namespace InkPresentationSample_Palette
{
    public delegate void PaletteEventHandler(Color color);

    public class UCColorPalette : Control
    {

아래와 같은 프로퍼티를 추가해 줍니다.

// 이벤트
public event PaletteEventHandler ColorChanged;

이벤트 하나 작성합니다.

void OnColorChanged()
{
    if (ColorChanged != null)
    {
        ColorChanged(_color);
    }
}

자 이제 이 함수를 적당한 곳에서 호출해 주기만 하면 이벤트 준비가 끝나겠죠?
어디가 적당할까요? 사용자가 ColorSet 중의 색상을 클릭할 때가 되겠죠!

AddColor 함수로 돌아가 아래와 같이 이벤트에 대한 함수를 작성합니다.

public void AddColor(Color color)
{
    // 색상 리스트에 추가
    _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을 정의합니다.
항상 보이기와, 토글 두 가지의 스타일을 지원합니다.

namespace InkPresentationSample_Palette
{
    public enum Style { AlwaysShow, Toggle }
    public delegate void PaletteEventHandler(Color color);

    public class UCColorPalette : Control
    {

프로퍼티를 추가합니다.

// 토글
Style _Style = Style.Toggle;
bool _bToggleShow = false;

여기서는 동적으로 Style을 변경할 수 있게 지원 안하고 ^^;
생성할 당시에 운명이 정해지도록 하겠습니다.
생성자에 아래와 같이 작업을 합니다.

public UCColorPalette(Style 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가 되게 합니다.

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);
    HidePalette();

    // 이벤트 발생
    OnColorChanged();
}

자 이제 마지막으로 이 컨트롤을 가지고 작업할 프로그래머 편의를 위한 함수를
2개 정도만 추가하고 마무리합니다.

이 Palette가 현재 어떤 어떤 색상을 가지고 있는지 알 수 없기 때문에,
Index를 통해서 색상을 지정해 줄 수 있는 SelectColor(int nIndex) 함수와,
User Control의 위치를 손쉽게 변경할 수 있게 Position(Point pt) 함수를
서비스로 작성해 줍니다.

public void SelectColor(int nIndex)
{
    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에서

UCColorPalette _oPalette = null;

Page_Loaded 함수 안에서 Palette를 생성합니다.
CreateInkEditor보다 먼저 생성해야 합니다.
이유는 곧 아시게 됩니다.

public void Page_Loaded(object o, EventArgs e)
{
    ....

    // 배경이미지 준비
    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에서 외부에서 색상을 입력 받을 프로퍼티를 추가합니다.

public Color StrokeColor { get; set; }

그리고 새 Stroke를 생성할 때 마다 그 색상을 가져다 쓰도록 합니다.

Stroke AllocStroke()
{
    // 스트로크 준비 흰색
    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로 돌아와 못다한 일을 마칩니다.

void _oPalette_ColorChanged(Color color)
{
    _oInkEditor.StrokeColor = color;
}

마지막으로 새 작품을 만들 때, UCInk의 Stroke가 팔레트에 현재 색상을
가져다 쓸 수 있도록 CreateInkEditor 함수를 손봐줍니다.

void 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를 먼저 호출해 주셔야 한다고
그랬던 겁니다.

자, 끝났습니다.


별거 아닌데 말이 너무 길었습니다. ^^;;;
감사합니다.

이올린에 북마크하기(0) 이올린에 추천하기(0)

Tag : ColorPalette, InkPresenter, RIA, Silverlight, UserControl, 방명록만들기, 실버라이트,

  1. taeyo

    강좌가 상당히 깔끔하네요^^
    인상적입니다

    | 2007/11/08 16:12 | PERMALINK | EDIT | REPLY |
  2. BlogIcon 길버트

    감사합니다. 혹시 taeyo.net의 taeyo님 이신가요?
    영광입니다!

    | 2007/11/08 23:07 | PERMALINK | EDIT | REPLY |
코멘트를 남겨 주세요. (Write your message and submit)
1. InkPresenter 객체 겉핥기 (InkPresenter XAML 구조)
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 객체를 추가합니다.

<Canvas xmlns="http://schemas.microsoft.com/client/2007"
        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 클래스에 아래 프로퍼티를 추가합니다.

InkPresenter _ink;
Canvas _cvMyself;

생성자를 아래와 같이 변경합니다.

public UCInk()
{
    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의 배경이미지와 편집 가능여부를 자유롭게 변경하기 위해 아래 프로퍼티를 또 추가합니다.

public bool Editable { get; set; }

Uri _BackImage = null;
public Uri BackImage { get { return _BackImage; } set { SetBackgroundImage(value); } }

기본 생성자를 아래와 같이 변경하고,
SetBackgroundImage, UpdateSize 함수를 추가합니다.

public UCInk(Size sz, bool bEditable)
{
    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부에서 많이 익숙해지신 아래 녀석들을 클래스 프로퍼티로 추가합니다.

// Ink 쓰기와 관련
Stroke _strokeCurrent = null;
StrokeCollection _oUndoBuffer = new StrokeCollection();


3. Ink 쓰기 구현

3부에서 했던 것이라 쉽습니다.

생성자에 Ink 쓰기 이벤트 4종 세트를 추가합니다.

// InkPresenter Mouse Event 필수 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을 체크하고 있습니다.

void ink_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
    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 AllocStroke()
{
    // 스트로크 준비 흰색
    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)

public void Undo()
{
    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부에서 또 뵙겠습니다.


이올린에 북마크하기(0) 이올린에 추천하기(0)

Tag : InkPresenter, RIA, Silverlight, UserControl, 방명록만들기, 실버라이트,

코멘트를 남겨 주세요. (Write your message and submit)
1. InkPresenter 객체 겉핥기 (InkPresenter XAML 구조)
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 클래스에 멤버 변수로 아래 것을 추가합니다.

// Undo, Redo용 버퍼
StrokeCollection UndoBuffer = new StrokeCollection();


3. Undo, Redo, Clear 버튼 이벤트

Page.xaml.cs 파일의 Page_Loaded 함수에 아래를 추가하고

// Undo, Redo Event
cvUndo.MouseLeftButtonUp += new MouseEventHandler(cvUndo_MouseLeftButtonUp);
cvRedo.MouseLeftButtonUp += new MouseEventHandler(cvRedo_MouseLeftButtonUp);

// Clear Event
cvClear.MouseLeftButtonUp += new MouseEventHandler(cvClear_MouseLeftButtonUp);

각각의 함수를 아래와 같이 구현합니다.

// Undo
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를 구현합니다.

void FlushUndoBuffer()
{
    UndoBuffer.Clear();
}

이제 이걸 언제, 어디서 불러줘야 할까요?
맞습니다. 새로 획을 그을 때마다 불러줘야 합니다.

2부에서 구현했던 StartNewStroke() 함수와
조금 전 구현한 Clear 버튼 MouseLeftButtonUp 이벤트 함수에 살포시 끼워 넣습니다.

void StartNewStroke()
{
    _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부도 기대해 주세요.
감사합니다. 꾸벅!

이올린에 북마크하기(0) 이올린에 추천하기(0)

Tag : InkPresenter, Redo, RIA, Silverlight, Undo, 방명록만들기, 실버라이트,

코멘트를 남겨 주세요. (Write your message and submit)
1. InkPresenter 객체 겉핥기 (InkPresenter XAML 구조)
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 만들기


안녕하세요. 강좌 2부네요!


오늘은 아래와 같은 Sample 예제를 만드는 과정을 설명하고자 합니다.

사용자 삽입 이미지























샘플을 작동해보실 수 있는 모델하우스 :
http://www.shiverlight.net/InkPresenterSample/InkPresenterSample


1. Page.xaml

Xaml은 Expression에서 다음과 같이 디자인을 했습니다.

사용자 삽입 이미지


사용자 삽입 이미지

















cvContent - 일단 화면 리사이즈 시 화면 중앙으로 쉽게 센터링 해주기 위해 캔버스를 만들고
이후에 만드는 모든 객체를 이 안에 넣었습니다.

ink - 강좌1부에서 설명드린 방법으로 InkPresenter 객체를 하나 추가해 두었습니다.

나머지 - 카페 링크들을 달 버튼으로 별로 중요한 것이 아닙니다.

여기서 중요한 점은 InkPresenter 위를 덮는 객체가 없게 하는 것입니다.
나머지는 별로 상관없습니다.

또, 나중에 InkPresenter객체에 Mouse Event를  추가하게 되는데, 이때 InkPresenter객체가
투명하면, Mouse Event들이 발생하지 않기 때문에 아래와 같은 방법들을 생각해 볼 수
있습니다.

1. InkPresenter에 Background를 설정해준다.
   - Background Property 에 색상만 할당
   - 또는, ImageBrush, VideoBrush 등의 Brush로도 가능


2. 사이즈와 위치가 똑같은 다른 객체(Canvas, Rectangle 따위)를 만들어 놓고,
   그 객체로부터 Mouse Event를 발생시켜서 사용한다.
  
조금 풀어서 설명을 드리면 예제처럼 배경에 사진 영역 위에 InkPresenter를 사용하고 싶을땐,
투명 InkPresenter에 ImageBrush를 이용해서 처리를 하고 InkPresenter의 Mouse Event를
사용하거나, 임의의 Canvas에 Image 넣고, InkPresenter도 넣은 다음 그 Canvas의 Mouse
Event를 사용하시면 됩니다.

2번 방법이 직관적이고 쉽다고 생각합니다만, 엄한 Canvas에서 Mouse Event를
받는 게 맘에 안들어 이 강좌에서는 1번 방법으로 해보겠습니다.

일단, 사진을 배경으로 깔기 위해 ImageBrush를 XAML 파일에 바로 적용하는 방법이 있습니다.
XAML 편집기에서 아래와 같이 적는 것이지요.

<InkPresenter x:Name="ink" Width="600" Height="400" Canvas.Left="5" Canvas.Top="5">
   <InkPresenter.Background>
      <ImageBrush ImageSource="BG_InkPresenter.jpg"/>
   </InkPresenter.Background>
</InkPresenter>


그 결과 프로그램 실행시 작동은 잘합니다만, Blend의 Design모드로 보면 이렇게 표시됩니다.

사용자 삽입 이미지
















더 이상의 디자인이 불가능해지는 것이지요.
Blend2가 지금은 Preview 버전이라 InkPresenter 객체에한 위지위그 기능은 아직 지원을
못하고 있는 것 같습니다.
그래서 일단 아래 상태로 놔두고 나중에 동적으로 ImageBrush를 설정해 주도록 하였습니다.

<InkPresenter x:Name="ink" Width="600" Height="400" Canvas.Left="5" Canvas.Top="5"/>


2. InkPresenter에 ImageBrush로 Background 깔기

Page.xaml.cs 파일 Page_Loaded(...) 함수 안에

// ink에 이미지 브러쉬로 Fill
ImageBrush ib = new ImageBrush();
ib.ImageSource = new Uri("BG_InkPresenter.jpg", UriKind.Relative);
ink.Background = ib;


3. 필요한 멤버 변수

Page 클래스의 멤버 변수로 아래 변수들을 추가합니다.
Page.xaml.cs 파일 Page 클래스 구현 상단에

// 스트로크
Stroke _strokeCurrent = null;

_strokeCurrent : 현재 사용자가 긋고 있는 획입니다.


4. MouseEvent

InkPresenter 위에 획을 긋기 위해서는 아래와 같은 Event 5종 세트를 사용하여야 합니다.

Page.xaml.cs 파일 Page_Loaded(...) 함수 안에

// InkPresenter Mouse Event 필수 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);

각 이벤트에서 하는 일을 살펴 보면,

1) MouseLeftButtonDown 이벤트

마우스 버튼이 클릭되면 CaptureMouse를 해주고,
새 획(Stroke)를 미리 생성합니다.
새 획 생성 방법은 5. 새 Stroke 생성해주기에서 설명하겠습니다.

void ink_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
    ink.CaptureMouse();
    StartNewStroke();
}

2) MouseMove 이벤트

긋고 있는 획이 있다면, 움직임에 대한 Stylus Points를 얻어서 획(Stroke)에 누적추가해
줍니다. 즉, 획이 그어집니다.

참고> 타블렛 PC에서 Stylus 펜을 사용할 때는 이 GetStylusPoints 함수가
좀더 많은 Points를 얻어다 줍니다.

void ink_MouseMove(object sender, MouseEventArgs e)
{
    if (_strokeCurrent != null)
    {
        _strokeCurrent.StylusPoints.AddStylusPoints(e.GetStylusPoints(ink));
    }
}

3) MouseLeftButtonUp 이벤트

ReleaseMouseCapture를 호출하고, 현재 긋고 있는 획에 null을 할당합니다.
즉, 그만 그립니다.

void ink_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
    ink.ReleaseMouseCapture();
    _strokeCurrent = null;
}

4) MouseLeave 이벤트

혹시 획을 긋다가 InkPresenter 영역 밖으로 나가면,
현재 긋고 있는 획에 null을 할당합니다. 즉, 그만 그립니다.

참고> 이 이벤트를 생략하시면, 영역 바깥까지 그림이 그려집니다.

void ink_MouseLeave(object sender, EventArgs e)
{
    _strokeCurrent = null;
}


5. 새 Stroke 생성해주기

MouseLeftButtonDown 이벤트에서 StartNewStroke를 호출합니다.
그러면 새 Stroke를 만들어서, 현재 획(_strokeCurrent)라고 할당을 해주고,
ink의 Stroke Collection에 추가해 줍니다.
Stroke의 스타일은 1부의 XAML 설명에서 보셨듯이, DrawingAttributes를 통해
설정하시면 됩니다.

void StartNewStroke()
{
   _strokeCurrent = AllocStroke();
   ink.Strokes.Add(_strokeCurrent);
}

Stroke AllocStroke()
{
    // 흰색 스트로크
    Stroke stroke = new Stroke();
    stroke.DrawingAttributes.Color = Colors.White;
    stroke.DrawingAttributes.OutlineColor = Colors.Black;
    stroke.DrawingAttributes.Width = 3;
    stroke.DrawingAttributes.Height = 3;

    return stroke;
}

감사합니다.

3부 기대해 주세요.






이올린에 북마크하기(0) 이올린에 추천하기(0)

Tag : InkPresenter, RIA, Silverlight, Stroke, 방명록만들기, 실버라이트,

코멘트를 남겨 주세요. (Write your message and submit)
1. InkPresenter 객체 겉핥기 (InkPresenter XAML 구조)
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 만들기

올해 마지막 당일치기 예비군 훈련 다녀왔습니다. ^^

1) InkPresenter XAML

사용자 삽입 이미지













일단 위와 같이 간단하게 두 획을 그은 경우를 예를 들어 설명을 드리겠습니다.

기본적으로 InkPresenter 객체는 배경색을 주지 않으면 투명하게 존재합니다.

즉, 동영상(MediaElement)이나 사진(Image)을 배경으로 구현하는 것도 가능하다는 것입니다.


위 그림에 대한 XAML을 살펴 보겠습니다.

   <InkPresenter Width="190" Height="190" Background="#FFB7E31D">
    <InkPresenter.Strokes>
      <Stroke>
        <Stroke.DrawingAttributes>
          <DrawingAttributes Color="#FFFFFFFF" Width="9" Height="9"/>
        </Stroke.DrawingAttributes>
        <Stroke.StylusPoints>
          <StylusPoint X="70" Y="75"/>
          <StylusPoint X="71" Y="75"/>
          <StylusPoint X="71" Y="76"/>
          <StylusPoint X="72" Y="76"/>
          <StylusPoint X="72" Y="77"/>
          <StylusPoint X="73" Y="78"/>
          <StylusPoint X="74" Y="79"/>
          <StylusPoint X="74" Y="80"/>
          <StylusPoint X="75" Y="81"/>
        </Stroke.StylusPoints>
      </Stroke>
      <Stroke>
        <Stroke.DrawingAttributes>
          <DrawingAttributes Color="#FF000000" Width="9" Height="9"/>
        </Stroke.DrawingAttributes>
        <Stroke.StylusPoints>
          <StylusPoint X="85" Y="83"/>
          <StylusPoint X="85" Y="82"/>
          <StylusPoint X="86" Y="81"/>
          <StylusPoint X="87" Y="80"/>
          <StylusPoint X="88" Y="79"/>
          <StylusPoint X="90" Y="78"/>
          <StylusPoint X="90" Y="77"/>
        </Stroke.StylusPoints>
      </Stroke>
    </InkPresenter.Strokes>
  </InkPresenter>


InkPresenter는 Stroke들의 Collection이라고 보시면 되구요.

Stroke는 한 획이라고 보시면 됩니다. (= 한 번의 클릭으로 그려지는 단위)


한 Stroke는 DrawingAttributes와 StylusPoints로 이루어 집니다.

DrawingAttributes를 통해 Ink의 Width, HeightColor를 설정할 수 있습니다.

StylusPoints는 마우스 움직이는 동안에 일정 Frequency로 샘플링된 점들의 집합이라고 보시면 됩니다.

타블렛에서 스타일러스를 사용해서 그리게 되면 좀더 많은 점을 샘플링하기 때문에 부드러운 그림이 그려지게 됩니다.



주의) 위 XAML코드는 Expression Blend에서 Invalid XAML 에러가 납니다.

하지만 실행시켜보면 화면에는 정상적으로 보여집니다. 버그일까요?


사용자 삽입 이미지












2) Expression Blend에서 InkPresenter 객체 추가하기

Visual Studio의 XAML 코드편집창에 <InkPresenter> 태그를 직접 입력해서 추가하시거나.

C# 코드로 동적으로 생성하셔도 되지만, 뭐니뭐니해도 Blend를 써서 하는게 디자인하기는 편하겠죠!



  A. Expression Blend를 실행시키고 왼쪽의 세로 Toolbar의 맨 아래쪽 Asset Library 버튼을 누릅니다.


  

사용자 삽입 이미지



  B. 오른쪽 위에 있는 Show All 체크 박스를 체크하시고, InkPresenter를 선택합니다.


사용자 삽입 이미지


  C. 왼쪽 세로 ToolBar에 InkPresenter 아이콘이 추가된 모습을 확인하실 수 있습니다.


사용자 삽입 이미지



  D. InkPresenter 아이콘을 클릭하고 Rectangle이나 Canvas를 추가하듯 추가합