GSI

사용자가 지정한 여러가지 정보들을 ListBox에 표현하는 방법을 기술합니다.
하나의 사용자 정보에는 여러개의 정보가 포함되어 있으며,
그 정보를 ObservableCollection 와 같은 Collection의 객체로 리스트화 합니다.

본 내용은 WPF Application을 기초로 합니다.

** 사용자 데이터 구성**

public class Place
{
    private string _name;

    private string _state;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public string State
    {
        get { return _state; }
        set { _state = value; }
    }

    public Place(string name, string state)
    {
        this._name = name;
        this._state = state;
    }

    public override string ToString()
    {
        //return base.ToString();
        return _name.ToString();
    }
}

public class Places : ObservableCollection<Place>
{
    public Places()
    {
        Add(new Place("Bellevue", "WA"));
        Add(new Place("Gold Beach", "OR"));
        Add(new Place("Kirkland", "WA"));
        Add(new Place("Los Angeles", "CA"));
        Add(new Place("Portland", "ME"));
        Add(new Place("Portland", "OR"));
        Add(new Place("Redmond", "WA"));
        Add(new Place("San Diego", "CA"));
        Add(new Place("San Francisco", "CA"));
        Add(new Place("San Jose", "CA"));
        Add(new Place("Seattle", "WA"));
    }
}


**xaml에서 사용방법**
1. 네임 스페이스 추가
xmlns:c="clr-namespace:WpfApplication1"

2. Window.Resources에 클래스 등록
<Window.Resources>
        <c:Places x:Key="PlacesData" />
        ................
</Window.Resources>


3. 화면 구성
<StackPanel>
      <TextBlock FontSize="18" Margin="5" FontWeight="Bold"
                 HorizontalAlignment="Center">Data Trigger Sample</TextBlock>
      <ListBox Width="180" HorizontalAlignment="Center" Background="Honeydew"
                 ItemsSource="{Binding Source={StaticResource PlacesData}}"/>
</StackPanel>


아래와 같은 화면으로 구성이 됩니다.

사용자 삽입 이미지


위의 내용들을 보시면 하나같이 WpfApplication1.Place 라고 나옵니다.
이것은 Collection 내부에 있는 오브젝트의 값을 ToString를 통해서 기본 값을 가져 오기 때문인데요(말이 좀 그렇다^^) 여기서 ToString()를 오버라이트 해서 _name의 값을 리턴 하도록 해보겠습니다.

Place 클래스 내부에 ToString()를 추가합니다.
public override string ToString()
{
     return base.ToString();
}

추가를 하게 되면 위와 같은 코드가 자동으로 추가가 됩니다.

여기서 return base.ToString()return _name.ToString()로 바꾸어 주면
위의 내용이 아니라 이름으로된 결과물을 보실수 있을겁니다.

이후에는 Place에 있는 정보들을 다 보여 주는 템플릿을 구성할 수 있습니다. ^^

본 예제는 msdn 로컬 파일주소를 참조 하세요.
ms-help://MS.MSDNQTR.v90.en/wpf_conceptual/html/0f4d9f8c-0230-4013-bd7b-e8e7fed01b4a.htm
(물론 웹에도 같은게 있습니다. 주소는 직접 찾기 바랍니다. ^^..)
Posted by gsi
:

본 내용은 msdn에 있는
How to: Control When the TextBox Text Updates the Sources 에 대한 내용을
조금 나름대로 이해 할 수 있도록 정리한 것입니다.
영어는 잘 몰라서 번역 수준은 아닙니다.


TextBox의 Text는 기본적으로 UpdateSourceTrigger 의 값이 LostFocus로 되어 있다고 합니다. 그래서 Text를 다 적고 나서 마우스나 기타 다른 것을 통해서 Focus가 다른 데로 갔을때 업데이트가 되도록 되어 있습니다.

하지만 이 것을 Text를 입력 하고 있는 중간에 계속 해서 업데이트가 가능하게 할려면
UpdateSourceTrigger를 수정해줘야 하는거 같습니다.

즉, TextBox 의 Binding 속성을 사용해서 UpdateSourceTrigger의 값을 PropertyChanged로 수정해주어야 되는거 같네요.

블렌드에서 TextBox와 TextBlock을 사용해서 값을 Data Binding 해서 테스트를 해보면
값이 바뀌는것을 볼 수 있습니다.

하지만 지금 예제는 cs 파일을 통해서 연동하는 부분을 살펴 보게 되겠습니다.

Person.cs 파일을 하나 생성하게 되는데요.

1. 네임 스페이스 지정
using System.ComponentModel;

2. INotifyPropertyChanged를 상속 받는 클래스 생성
class Person : INotifyPropertyChanged
{
}


3. 데이터 값으로 name를 생성
private string name;

4. PropertyChangedEventHandler 이벤트 핸들 생성
public event PropertyChangedEventHandler PropertyChanged;

5. name를 사용할 인터페이스(??)를 생성, get, set 두개 지정
public string PersonName
{
    get { return name; }
    set
    {
        name = value;

        OnPropertyChanged("PersonName");
    }
}


6. PropertyChanged 를 처리할 함수 생성
protected void OnPropertyChanged(string name)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(name));
    }
}

cs 파일을 만든 다음에 xaml을 처리 합니다.
1. Person.cs 파일을 사용하기 위해서 네임 스페이스를 지정
xmlns:src="clr-namespace:UntitledProject1"

2. Window.Resources 에 Person의 키를 생성
<src:Person x:Key="myDataSource" PersonName="Joe"/>
 - x:Key="myDataSource" 를 하게 되면 Person의 생성자가 호출되는거 같네요.
 - PersonName="Joe" 를 하게 되면 public string PersonName 의 set 함수가
호출되는거 같습니다.

3. TextBox의 Text 속성에 바인딩 시킵니다.
<TextBox.Text>
    <Binding Source="{StaticResource myDataSource}" Path="PersonName"
                   UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
 - Source에는 Window.Resource에 Person을 선언해 놓은 myDataSource를 적용합니다. Path에는 PersonName를 추가 하게 되는데요. PersonName는 Person.cs 파일에 존재 하는 것입니다. (만약 xml을 사용하게 된다면 Path가 아니라 XPath가 되며 "PersonName"이 아니라 @"PersonName"가 됩니다.)
 - UpdateSourceTrigger="PropertyChanged"를 셋팅해 주게 됩니다.

4. TextBlock 의 Text 속성에 바인딩 시킵니다.
<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>
 - UpdateSourceTrigger 는 지정하지 않고 TextBox와 동일하게 구성합니다.
 - 위의 코드는 아래와 같이도 쓸수 있겠네요.
<TextBlock>
    <TextBlock.Text>
        <Binding Source="{StaticResource myDataSource}" Path="PersonName"/>
    </TextBlock.Text>
</TextBlock>


-----------------------------------------------------
작동 흐름을 대충 찍어 보면..

1. <src:Person x:Key="myDataSource" PersonName="Joe"/>
여기를 거치게 되면서
- public Person() 생성자가 호출 됩니다.
- public string PersonName 의 set에 value로 "Joe"가 입력 됩니다.
- OnPropertyChanged("PersonName"); 가 호출됩니다.
- OnPropertyChanged 내부에서는 handler가 null 이라서 그냥 빠져 나가네요.

2. Path="PersonName"
- 이 구문을 만나게 되면서 public string PersonName 이 호출되고 여기에서 name의 값을 get으로 리턴 시켜 줍니다.

3. TextBox의 Text에 내용 입니다.
- public string PersonName 의 set 가 호출 됩니다.
- OnPropertyChanged("PersonName"); 를 호출합니다.
- OnPropertyChanged() 내부에서 handler가 null이 아니게 되며, handler()를 호출하게 됩니다.
- public string PersonName 가 호출 되게 되는데요.. 음 아무래도 여기서, xaml의 코드를 거쳐서 TextBlock의 Text의 값이 변하게 되는거 같습니다.

**이상 간단하게 봤습니다.**

WPF를 하면서 C#의 스킬 부족과 영어 울렁증, xaml의 코드 난해함에.. 많이 헤매게 되는거 같습니다. 빨리 익혀서 이것저것 만들어 보는 날이 왔으면 좋겠네요 ^^.

강좌가 조금 난잡해 진거 같은데요.. ^^..
본 강좌의 소스는 인터넷의 msdn 사이트나 msdn을 설치 하셨다면 아래 주소에 있습니다.^^
ms-help://MS.MSDNQTR.v90.en/wpf_samples/html/b2906631-8a4b-43b4-b077-9e732a8ff363.htm (로컬msdn)

잘못된 부분이나 조금 도움이 될만한 내용이 있으시다면 적어 주시면 고맙겠습니다. ^^

Posted by gsi
:

화면에 보여지는 객체로 여기지 않고 Resource로 처리 하면서
화면의 다른 객체에 표현 가능하게 할려면 아래와 같은 코드로 변형 하면 됩니다.
이때 Grid의 x:Key 가 부여 되어야 합니다. ^^

 <Window.Resources>
  <Grid x:Key="KTestGrid" HorizontalAlignment="Left" Margin="38.857,8,0,0" x:Name="TestGrid" VerticalAlignment="Top" Width="151.786" Height="126.286">
   <Rectangle Margin="19.643,20.929,28.572,30.357" Stroke="#FF000000">
    <Rectangle.Fill>
     <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
      <GradientStop Color="#FFFF3838" Offset="0"/>
      <GradientStop Color="#FFFFFFFF" Offset="1"/>
     </LinearGradientBrush>
    </Rectangle.Fill>
   </Rectangle>
  </Grid>
 </Window.Resources>
Posted by gsi
:

VisualBrush 사용한 Grid 객체 렌더링 하기.


2D 형태의 객체를 3D 화면에 렌더링 하는 방법을 적을려고 합니다.
이때 사용하게 되는게 VisualBrush가 있는데요.
아래의 사용법을 가지고 처리해줄 수가 있습니다.


Window.Resource에 등록해서 할수도 있을듯 한데요.
이번 예제는 화면에 그려지고 있는 Grid를 3D 화면에 그려 보는것으로 마치겠습니다.

Start!!!

프로젝트를 생성합니다.
아래와 같이 WPF Application(*.exe)를 선택 합니다.
Name는 VisualBrushTest로 입력하고 Ok를 누릅니다.
사용자 삽입 이미지

Window를 선택하고 배경색은 검정 계통으로 보이게 합니다.
흰색일때는 왠지 테스트 하기가 불편한듯 해서 ^^.
사용자 삽입 이미지

VisualBrush로 사용할 Grid 객체를 선택하고 생성합니다.
사용자 삽입 이미지
아래와 같이 테스트를 위해서 간단한 사각형을 Grid에 추가 하고 Grid의 이름을 부여 합니다.
사용자 삽입 이미지

Viewport3D 객체를 추가 해야 하는데요.
사실 이게 직접 xaml을 코딩 하기도 좀 번거롭고 해서 Tools에 있는 Make Image 3D
를 사용해 보겠습니다.
"TestGrid"를 선택하고 Make Image 3D를 바로 하면 좋겠지만. 이게 되지를 않습니다.
사용자 삽입 이미지

그래서 아래와 같이 우측의 Project 탭을 선택하고 VisualBrushTest를 선택한 후에
마우스 오른쪽 버튼을 클릭해서 컨텍스트 메뉴를 뛰우게 됩니다.
그리고 나서 Add Existing Item...를 선택 하게 됩니다.
선택후에 나오는 Add Existing Item 창에서 샘플로 사용할 이미지를 선택 합니다.
이 이미지는 나중에 지워도 괜찮은거니 아무거나 해주심 됩니다. ^^
사용자 삽입 이미지

그림 샘플에 있는 이미지를 추가 해봤습니다. 아래와 같이 이미지가 추가 되었죠.
사용자 삽입 이미지

이 이미지를 선택한 후에 컨텍스트 메뉴에서 Insert를 해주면 화면에 표시가 되게 됩니다.
사용자 삽입 이미지

이제 Viewport3D를 만들어 보겠습니다.
Image 객체는 이 메뉴가 활성화 되어서 보이게 됩니다.
Tools>Make Image 3D...를 선택 합니다. 이제 Objects and Timeline 화면에 보시면
Image 가 Viewport3D로 되어 있는 것을 볼 수 있습니다.
중앙의 윈도우 화면의 이미지는 그대로 보이게 되죠.
트랜스폼이나 기타 조작을 해주지 않았으니 당연하겠죠 ^^.
사용자 삽입 이미지

화면을 3차원인거 처름 보이게 하기 위해서 조금 트랜스폼을 조작해 보겠습니다.
여기서는 방법이 몇가지 될수 있겠죠.
좌측의 메뉴를 이용해서 하는 방법을 우선 보겠습니다.
Camera Orbit 메뉴를 선택 하고 좌측의 Viewport3D를 선택하고 객체를 3차원으로
변형할 수 있습니다.
사용자 삽입 이미지
[참고] 위에서 보시는 이미지를 보면 Viewport3D의 화면 영역 안에서 clip되는걸
볼수 있습니다. 이것을 없애고 싶다면 아래의 체크 박스를 없애면 됩니다.
사용자 삽입 이미지

이제 샘플로 사용한 이미지를 없애고 위에서 작업한 Grid를 추가해 보겠습니다.
Design에서 XAML 페이지로 이동하고 나면 좀 많은 코드가 생기게 됩니다.
아까 "푸른언덕.jpg"를 추가 했으니 그쪽으로 가면 되겠죠...
사용자 삽입 이미지
위에서 보는 것과 같이 DiffuseMaterial.Brush에 VisualBrush를 붙여 보겠습니다.
사실 DiffuseMaterial 말고도 몇개 더 있다는 것을 3D를 해보신 분이라면 아실겁니다. ^^
자. 이제 아래와 같이 이 부분을 수정하게 됩니다. 우리가 아까 Grid의 이름을
TestGrid로 한거 기억 나시죠?. 아래와 같이 입력합니다.
사용자 삽입 이미지
아래와 같이 화면이 Grid와 같이 나오게 됩니다.
사용자 삽입 이미지

Blend 툴에서 Grid의 내용을 수정하거나 하면 동적으로 Viewport3D의 화면도
바뀌게 되어 있습니다.
보통 이게 동영상이나 기타반사 효과 또는 젖은 효과라고도 하던데요.
이런 트낌을 줄때 많이들 하게 되는거 같아요.

여기까지 해보신 분이라면 많은 생각이 될게 될거 같아요.
몇가지 개선 사항을 적어 볼까 합니다.

1. TestGrid를 Window.Resource에 추가 해서 화면에 보이지는 않지만 내부 데이터를
   접근하고 하면서 화면에는 Viewport3D 의 형태로 표현도 가능하죠.
2. 그리고 3DTools 라이브러리를 사용 하게 되면 Window Form도 여기에 붙여서
    3D 상에서의 윈도우 컨트롤이 가능하게 됩니다.

이거 말고도 다양한 효과를 줄 수 있을거 같아요.
이번 강좌는 여기까지 하고 3DTools를 이용한 외국 사이트의 내용을 나름대로
정리 해서 올릴까 합니다. 어쩔지 ^^.. 많은 의견 주세요.

Posted by gsi
:

Storyboard 를 사용한 TransformGroup를 C#에서 처리 하는 방법


Blend에서 Timeline을 생성하고 오브젝트를 움직이면 에니메이션 처리를 할수 있다.
가장 간단하게 하면서도 효과적인 에니메이션을 구현할 수 있다.

에니메이션 쪽을 보면 Transform에 여러개의 객체가 붙을수 있다.

만약 Button 의 이름이 btn이라고 하고 예로 들어 보겠다.

btn.RenderTransform 에 붙을수 있는 것은 아래와 같을 것이다.

btn.RenderTransform = new RotateTransform(0);
btn.RenderTransform = new ScaleTransform(1, 1);
btn.RenderTransform = new SkewTransform(0, 0);
btn.RenderTransform = new TranslateTransform(10, 10);



그리고 위의 4개를 통합해서 사용할 수 있는것으로 TransformGroup를 들수 있다.

...
btn.RenderTransform = tGroup;



위에서 보는 것과 같이 5개의 속성을 연결 시킬 수가 있다.

몇가지 테스트를 해봤고,
RenderTransform에 TransformGroup를 붙이지 않고 개별적인
속성을 붙이게 되면 Animation할때도 그 속성에 대한 정보만 제어 할 수 있다.
즉, 이동만을 하고 싶은 것이라면 TranslateTransform만 연결해서 사용하면 될듯 하다.

구체적인 예제를 만들지는 않았지만.
궁금하면 질문 해주기 바랍니다.

아래의 코드는 4개의 속성 정보를 다 연동하고 난 후에 제어 하는
아주 심플한 예제 입니다.
물론 자료형을 사용해서 배열로 정보들을 저장하고 조금더
깔끔하고 효과적으로 처리 할 수도 있을거 같다.
그리고 현재 코드는 2D의 처리만 가능한 것이다.

-------------------------------------------------------------------------
전제조건 :
Blend에서 기본 오브젝트를 우선 제작한다.
1. 버튼 두개를 제작한다.
2. 텍스트 박스를 하나 만든다. 이름은 TextBox2로 지정한다.

자.. 프로그램 코드는 *.cs 파일에서만 구동되므로 바로 설명 하겠다.

제어에 필요한 변수를 아래와 같이 만들었습니다.

//에니메이션 이름에 사용할 것을 지정
private string aniTrans = "_aniTrans";
private string aniSkew = "_aniSkew";
private string aniScale = "_aniScale";
private string aniRotate = "_aniRotate";

//트랜스폼을 제어 하기 위해서 변수를 설정
private TransformGroup group;
private RotateTransform rotate;
private ScaleTransform scale;
private SkewTransform skew;
private TranslateTransform trans;

//스토리 보드의 속성정보를 저장할 변수 설정
private Storyboard transStoryX;
private Storyboard transStoryY;
private Storyboard RotateStory;

//에니메이션 변수 설정
private DoubleAnimation myTransX;
private DoubleAnimation myTransY;
private DoubleAnimation myRotate;

public Window1()
{
    ...
    group = new TransformGroup();
    rotate = new RotateTransform(0);
    scale = new ScaleTransform(1, 1);
    skew = new SkewTransform(0, 0);
    trans = new TranslateTransform(50, 50);

    group.Children.Add(rotate);
    group.Children.Add(scale);
    group.Children.Add(skew);
    group.Children.Add(trans);

    this.RegisterName(aniTrans, trans);
    this.RegisterName(aniSkew, skew);
    this.RegisterName(aniScale, scale);
    this.RegisterName(aniRotate, rotate);

    TextBox2.RenderTransform = group;
}

private void OnLoaded(object sender, RoutedEventArgs e)
{
    myTransX = new DoubleAnimation();
    myTransY = new DoubleAnimation();
    myRotate = new DoubleAnimation();

    Storyboard.SetTargetName(myTransX, aniTrans);
    Storyboard.SetTargetProperty(myTransX, new PropertyPath(TranslateTransform.XProperty));
    Storyboard.SetTargetName(myTransY, aniTrans);
    Storyboard.SetTargetProperty(myTransY, new PropertyPath(TranslateTransform.YProperty));
    Storyboard.SetTargetName(myRotate, aniRotate);
    Storyboard.SetTargetProperty(myRotate, new PropertyPath(RotateTransform.AngleProperty));

    transStoryX = new Storyboard();
    transStoryX.Children.Add(myTransX);
    transStoryY = new Storyboard();
    transStoryY.Children.Add(myTransY);
    RotateStory = new Storyboard();
    RotateStory.Children.Add(myRotate);
}

private void OnClick(object sender, RoutedEventArgs e)
{
    myTransX.To = -50;
    myTransX.Duration = new Duration(TimeSpan.FromMilliseconds(800));
    myTransY.To = -50;
    myTransY.Duration = new Duration(TimeSpan.FromMilliseconds(800));
    myRotate.To = -50;
    myRotate.Duration = new Duration(TimeSpan.FromMilliseconds(800));

    transStoryX.Begin(this);
    transStoryY.Begin(this);
    RotateStory.Begin(this);
}

private void OnClick2(object sender, RoutedEventArgs e)
{
    myTransX.To = 120;
    myTransX.Duration = new Duration(TimeSpan.FromMilliseconds(800));
    myTransY.To = 150;
    myTransY.Duration = new Duration(TimeSpan.FromMilliseconds(800));
    myRotate.To = 270;
    myRotate.Duration = new Duration(TimeSpan.FromMilliseconds(800));

    transStoryX.Begin(this);
    transStoryY.Begin(this);
    RotateStory.Begin(this);
}


아.. 내용이 많아 지네욤..

우선 한번만 지정하는 부분과 매번 동작상황에 맞게 값을 입력해줘야 하는것으로
세분화를 해봤습니다.
Windows1()에 있는 내용은 기본적으로 설정할 부분이며,

OnClick(), OnClick1() 에는 버튼에 대한 이벤트 입니다.
아무래서 여기서 From, To, Duration을 설정해 줘야 할듯 합니다.

아. 참고로 From을 설정하지 않으면 현재 위치에서 To까지의 진행을
하게 됩니다. 처음에 이게 궁금했는데 아주 편한듯 합니다. ^^

그리고 Storyboard 여기 설정하는 부분이 솔직이 아직 다 이해가 가질 않습니다.
위의 코드 처름 하나하나의 속성을 처리 하는 부분에서는 별다른
문제가 없지만 path 형태의 처리는 아직 공부 중입니다. ^^

음.. 대충 설명은 이것으로 마치겠습니다.
샘플 받아서 실행해 보시고 분석해 보시면 더 도움 될거 같아욤.
아무쪼록 좋지 않은 내용을 봐주셔서 감사 ^^

Posted by gsi
:

Overview

A collection of tools to help developers using the 3D features in the Windows Presentation Foundation.

Some of things you will find inside:
  • Code to interact with 2D content placed on 3D
  • Trackball class to Rotate the camera using the mouse
  • ScreenSpaceLines3D to draw fixed width lines in a 3D scene
  • Texture coordinate generators

Current status: Released - please go to the "Releases" tab to download the latest source and binaries.
Audience: Beginner, Intermediate, Advanced
Language: C#
관련 주소 : http://www.codeplex.com/3DTools

내용.
해당 라이브러리를 사용하게 되면 3D의 표면에 Grid와 같은 패널의 내용을 맵핑 할 수가 있습니다. 3차원의 화면에 2차원의 각종 컨트롤을 처리 할 수가 있습니다.
그리고 트랙볼 구현도 쉽게 되니 활용하기 좋은 자료인듯 합니다.
Posted by gsi
: