Tech/Silverlight2011. 6. 22. 16:52

Silverlight 5에는 3D 그래픽 기능이 추가되었습니다. (기존에는 Silverlight에서 자체적으로 지원하지는 않고 서드파티 라이브러리만 있었습니다Silverlight 5 3D 그래픽은 XNA 그래픽 라이브러리를 사용하며 GPU 가속이 지원됩니다.

XNA는 
내부적으로 DirectX의 일부 기능을 사용하기 때문에 사용 환경에 제약이 있습니다.
 

  • DirectX 9.0 이상
  • Windows XP 이상 (XP, Vista, 7)
     

사용법은 직접 다루지는 않고 링크로 대체하도록 하겠습니다.

3D
튜토리얼

http://silverlight.bayprince.com/tutorials.php

 

3D 데모

http://david.blob.core.windows.net/babylon/Babylon.html

마우스를 이용해 카메라 시점을 변경할 있고 키보드 화살표를 이용해 이동할 있습니다.

 

Silverlight 3D 참고 자료: http://msdn.microsoft.com/en-us/library/gg197424(v=XNAGameStudio.35).aspx

Posted by 알 수 없는 사용자
Tech/Silverlight2011. 6. 22. 15:58

Silverlight 5에는 동영상을 재생할 하드웨어(GPU; 그래픽카드 ) 이용해 CPU 부하를 줄이는 기술이 추가되었습니다.

 

하드웨어 가속을 사용하는 법은 간단합니다.

실버라이트 파일(.xap) 파일을 명시한 <object> 태그 안에 <param>으로 enableGPUAcceleration 속성을 주면 됩니다.


enableGPUAcceleration 자체는 Silverlight 5 이전에도 원래 있었던 속성이고, 바뀐 점은 속성을 켰을 동영상 재생 GPU 가속을 사용하게 되었다는 점입니다.


Silverlight 5 Beta 이뤄진 간단한 성능 테스트입니다.

번째 사진은 Silverlight 없이 미디어 플레이어로 동영상을 재생했을 ,

번째 사진은 GPU 가속 없이 Silverlight 동영상을 재생했을 ,

번째 사진은 GPU 가속을 켜고 Silverlight 동영상을 재생했을 ,

번째 사진은 GPU 가속과 함께 BitmapCache 사용했을 때의 CPU 사용도입니다.

(높이가 낮을 수록 좋고, 좌우 길이는 성능과 관계 없습니다)


번째 사진과 비교했을 , GPU 가속을 것과 켜지 않은 것의 차이가 생각보다 크지 않은 것을 있습니다. 하지만, BitmapCache 사용했을 때는 CPU 사용도가 크게 낮아진 것을 확인할 있습니다.

BitmapCache 동영상 디코딩 자체에 영향을 주지는 않습니다만 GPU 가속을 사용할 함께 사용하면 이점이 있다고 합니다. (자세한 것은 아래의 원문을 참고하세요)

출처: http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2011/05/03/silverlight-5-beta-rough-notes-hardware-decoded-h-264.aspx

하드웨어 가속 디코딩에 대해서는 정식 릴리즈가 나올 때까지 많은 개선이 이루어질 거라고 생각하기 때문에, 이후 정식 버전을 기대해 봐야겠습니다.


 MediaElement BitmapCache 적용하는 방법은 다음과 같습니다.
 


캐시에 대한 다른 의견도 있습니다. 저사양 컴퓨터에서 MediaElement 캐시를 사용하면 성능이 하락한다는 의견인데 확인해 필요가 있을 같습니다.

출처: http://msdn.microsoft.com/en-us/library/ee309563(v=VS.96).aspx#1

Posted by 알 수 없는 사용자
Tech/Silverlight2011. 6. 21. 15:04

Silverlight 5(Beta) 동영상 재생 속도 조절 기능이 추가되었습니다.

MediaElement PlaybackRate 속성을 설정함으로써 쉽게 재생 속도를 조절할 있습니다.


PlaybackRate
의 값은 배속을 나타내며, 값이 2이면 2배속(2x), 0.5면 0.5배속(0.5x)으로 동작합니다.

이를 이용해서 배속 조절이 가능한 간단한 동영상 재생기를 만들어 보겠습니다.
 


위와
같이 동영상을 재생할 MediaElement 하나와 재생 속도를 조절할 Slider 하나를 만들고

MediaElement PlaybackRate 속성에 Slider Value 바인드하고 실행하면,


슬라이더를 이용해 동영상 재생 속도를 조절할 있는 동영상 재생기가 완성됩니다!

'Tech > Silverlight' 카테고리의 다른 글

[Silverlight 5] 3D 그래픽  (1) 2011.06.22
[Silverlight 5] GPU 가속 동영상 재생  (0) 2011.06.22
[Silverlight MEF - Export UserControl]  (2) 2010.06.02
[Silverlight MEF - Dynamic XAP Loading]  (1) 2010.05.12
Posted by 알 수 없는 사용자
Tech/Silverlight2010. 6. 2. 12:04

내가 하고자 하는 것은, Rectangle이 이동하는 애니메이션을 갖고 있는 UserControl만 있는 프로젝트를 만들고, 그 프로젝트의 결곽물인 .xap 파일을 다른 프로젝트에서 동적으로 로딩하여 애니메이션을 볼 수 있도록 하는 것이다.

이 과정에서 나는 MEF를 이용할 것이다. 동적으로 .xap파일을 로딩하는 방법은 MEF말고도 있다. WebClient를 이용하여 .xap파일을 열어 Assembly 정보를 얻는 방법도 그 중 하나이다.

동적으로 .xap를 로딩하는 방법은 지난 포스팅에 소개했었다. (예제 따라해보기지만...)
우선 처음으로 할 일은 Rectangle이 이동하는 애니메이션을 갖고 있는 UserControl만 있는 프로젝트를 만드는 일이다.
나는 간단하게 Rectangle을 생성한 뒤, Rectangle이 좌측에서 우측으로 이동하면서 색깔이 변하는 Storyboard를 만들었다.
그 다음엔 애니메이션을 재생하고 있는 UserControl을 통째로 Export한다.

[Export("Animation", typeof(UserControl))]
public partial class AnimationControl : UserControl

"Animation"이라는 특정 이름과 UserControl 타입으로 외부에 노출한다는 것을 명시해주자. 그리고 빌드 한 후, .xap 결과물을 Import할 프로젝트의 ClientBin 폴더에 복사한다.
Import할 프로젝트로 이동하여, Import 짝꿍을 만들어주자. 지난 포스팅에도 말했었지만, 외부 .xap파일을 로딩하는 경우이므로 [ImportMany]를 사용한다.

[ImportMany("Animation", typeof(UserControl), AllowRecomposition = true)]
public IEnumerable<UserControl> AnimationCtrl { get; set; }

위와 같이 정의하면, AnimationCtrl에 아까 Export한 AnimationControl이 들어갈 것이다. 내가 원하는 것은 AnimationControl을 현재 프로젝트의 LayoutRoot에 추가하여 애니메이션을 보는 것이기 때문에, AnimationCtrl을 foreach문으로 돌려 AnimationControl의 정보를 추출해낸다. 이 작업을 .xap파일 다운로드가 완료되면 수행하도록 해준다.

Package.DownloadPackageAsync(new Uri("RectangleAnimation.xap", UriKind.Relative), (s, p) => 
{    
    catalog.AddPackage(p);    
    foreach (var item in AnimationCtrl)    
    {        
        this.LayoutRoot.Children.Add(item);    
    }
});

여기까지 했다면, 내가 찾던 UserControl이 현재 화면에 보이고, 애니메이션이 실행될 것이다. 난 여기에서 그치지 않고, 애니메이션 실행이 완료된 후, 메시지 박스를 호출하도록 하겠다. 그러기 위해선, 몇 줄 더 첨가해야한다.

Package.DownloadPackageAsync(new Uri("RectangleAnimation.xap", UriKind.Relative), (s, p) => 
{
     catalog.AddPackage(p);

     foreach (var item in AnimationCtrl)
     {
          EventInfo eInfo = (item.GetType()).GetEvent("Completed");
          Type handlerType = eInfo.EventHandlerType;
          Delegate d = Delegate.CreateDelegate(handlerType, null, this.GetType().GetMethod("OnCompleted"));

          eInfo.AddEventHandler(item, d);

          this.LayoutRoot.Children.Add(item);
          }
     }
);

분홍색 박스 친 부분들이 추가된 부분이다. 난 애니메이션을 포함하고 있는 UserControl 내에서 미리 애니메이션 실행이 완료되면 외부로 Completed 이벤트를 알리도록 만들어 놓았다. 그리고 외부에서 EventInfo를 이용하여, Completed로 정의된 이벤트 정보를 얻도록 한 후, Completed 이벤트가 발생하면, OnCompleted 함수를 호출하도록 연결하였다.

public void OnCompleted(object sender, EventArgs e)
{
    MessageBox.Show("Animation Complete");
}

그 이후, 간단하게 OnCompleted 에서 메시지 박스를 호출하면 끝!
참 쉽죠잉.

Posted by 알 수 없는 사용자
Tech/Silverlight2010. 5. 12. 16:28

  이전에 XAP 로딩하는 문제로 MEF를 살펴본 적이 있었는데, 그 때에는 잘 이해하지 못해 포기했었다.
  그러다, 이제와서 다시 살펴 볼 기회가 생겨 MEF에 대해 조사하게 되었다.

  이제사 간신히 예제 하나를 따라잡기 해 본 수준(?)이라고 하겠다.

  다음의 자료에서 굉장히 간단하게 MEF 예제를 놓고 설명한 동영상 자료가 있다.
  Silverlight MEF 자료 동영상

  MEF로 동적으로 .xap 파일을 로딩하는 것만 할 수 있는 것은 아니지만, 이 부분에 대해서만 중점적으로 설명하도록 하겠다.

  우선 MEF는 Silverlight 4에서 포함되어 나올 예정이다. 현재 3버전에는 없기 때문에 따로 파일을 받아야만 쓸 수 있다. 나는 MEF Preview 6 버전을 이용하여 설명하도록 하겠다.
  MEF Preview 6
  위의 사이트에는 MEF Preview 6에 관련된 정보들에 대한 설명이 갖추어져 있다. 파일을 다운 받기 위해서는, 다음과 같이 "the CodePlex site" 링크 주소를 클릭하여 다운받으면 된다.



  파일 다운로드를 다 받은 후, 압축을 해제한다.
  자, 이제 준비는 되었다.

  우선 동영상 예제때로 따라해보도록 하겠다.

  첫 번째로 해야할 일은 .dll을 참조시키는 것이다. 아까 압축 해제한 폴더에서 MEF_Preview_6\bin\SL3 폴더 아래로 이동하면, System.ComponentModel.Composition.dll 파일이 있을 것이다. 이 .dll 파일을 참조시켜야 한다.

  그 다음은 Catalog 객체를 생성해야 하는데, 우선 System.ComponentModel.Composition.dll을 개체 브라우저로 확인해보자. 여러 가지 차암~ 많다. 그 중 Catalog객체만 보면, AggregateCatalog, AssemblyCatalog, TypeCatalog, ComposableCatalog, PackageCatalog들이 있다. (이 이외에도 다른 Catalog들이 있다.) 아직 이 Catalog들의 각각의 역할을 확인하지 못했다.
  나는 이제부터 PackageCatalog를 사용할 것이다.

var catalog = new PackageCatalog();
catalog.AddPackage(Package.Current);

  Package.Current는 현재 실행 중인 어셈블리의 Package이다. 이렇게 PackageCatalog를 생성하여 현재 실행 중인 어셈블리의 Package를 추가한 뒤, CompositionContainer를 생성한다.

var container = new CompositionContainer(catalog);
CompositionBatch batch = new CompositionBatch();
batch.AddPart(this);
container.Compose(batch);

  위와 같이 CompositionContainer를 만들어 이전에 생성한 PackageCatalog를 담는다. 그 후, 지금 내가 사용하고 있는 클래스 객체와 연결하기 위해 CompositionBatch를 생성하여, Compose를 하는 이 세 줄을 container.ComposeParts(this); 한 줄로 축약시킬 수 있다. 그럼 이후부턴 생성한 CompositionContainer를 통해 Export되는 요소들을 접근할 수 있게 된다. 무슨 의미냐 하면,

  MEF 에서는 외부로 노출시켜야 할 요소들에게 태그처럼 [Export]를 명시해준다. 예를 들어, Name이라는 이름의 Property를 외부에 노출시켜야 하는 경우, 다음과 같이 정의한다.

[Export("Name")]
public string Name
{
     get
     {
          return "Incheon";
     }
}

 "Name"이라는 특정 이름으로 Name 속성이 외부로 노출되었다. 이 Name 속성을 접근하고자 하는 경우에는, 아까 생성했던 CompositionContainer를 통해 접근할 수 있다. 예를 들어, CompositionContainer.GetExport<T>()를 사용하면 된다. 하지만 이런 식의 접근보다, 바로 접근할 수 있는 짝꿍을 만들어 주는 것이 더 효율적이다. [Export]의 짝꿍은 [Import]이다. 한 마디로 Export된 속성을 받아서 쓰는 입장 쪽에서 Import로 정의된 속성을 만들어주면 된다.

[Import("Name")]
public string Name { get; set; }

  이렇게 정의한 후에, 프로그램을 실행해보면, CompositionContainer의 Compose가 실행되자마자 Import로 정의된 Name 속성 값으로 Export로 정의된 Name 속성의 value가 들어가는 것을 확인할 수 있다. 그 다음부터는 간단하게 Name 속성을 그대로 사용하면 된다.

  자, 문제는 -_- 얘네들을 어떻게 쪼물딱 해서 .xap를 동적으로 로딩하는가에 대한 문제이다.

  우선 새로운 프로젝트를 생성하여, 클래스를 추가하고, 외부로 노출시킬 요소들을 만들자.

public class NameContainer
{
    [Export("Names")]
    public string name1 = "name1";
    [Export("Names")]
    public string name2 = "name2";
    [Export("Names")]
    public string name3 = "name3";
}

  위와 같이, NameContainer라는 이름의 클래스를 생성한 뒤, "Names"라는 이름으로 외부에 노출시킬 아이들을 몇 놈 만들었다. 나는 Names로 노출된 아이들을 ListBox에 정렬하여 표현할 생각이다. 우선 내가 본래 있던 프로젝트로 돌아와 Import 짝꿍을 만들자. 나는 [Import] 대신 [ImportMany]를 사용할 것이다. [ImportMany]를 사용하는 경우 중엔 Export 되는 상대가 여러놈일 때가 있다. 하지만, 지금의 경우에는 .xap파일을 로드하여 쓰기 때문이라고 생각하는 것이 맞을 것이다.

[ImportMany("Names", AllowRecomposition = true)]
public ObservableCollection<string> Names { get; set; }

  정확한지는 모르지만, AllowRecomposition 속성은 CompositionContainer와 연결되어있는 Export들의 상태가 변경될 때마다 Update 하는 것을 허락하는 지를 나타내는 것 같다. Recomposition에 대한 설명은 다음 싸이트에 있다. 
  Recomposition and Constructors

  ImportMany로 값을 얻는 경우에는 배열이나 Collection 형태 등으로 얻어야 한다. 나는 Names라는 이름의 ObaservableCollection에 "Names"로 Export되는 아이들을 넣을 것이다.
  문제는, Export로 정의된 요소들은 모두 다른 프로젝트에 있다. 내가 원하는 것은 Export 요소들을 담고 있는 프로젝트의 .xap 결과물만을 이용해서 내가 만들어 놓은 ListBox에 출력시키고 싶다. 이런 경우에는, Export 요소들을 담고 있는 프로젝트의 .xap 결과물을 내가 본래 작업하는 프로젝트의 Web 프로젝트 폴더 내에 있는 ClientBin 폴더 아래에 복사한다. 그 후, CompositionContainer 객체를 생성하기 전에,

Package.DownloadPackageAsync(new Uri("NameContainerProject.xap", UriKind.Relative), (s, p) => catalog.AddPackage(p));
  Package.DownloadPackageAsync를 이용하여, 새로이 생성했던 프로젝트의 .xap 결과물이 위치하고 있는 경로파일을 매개변수로 넘겨지면, .xap을 다운로드 하여 Package로 만들어준다. 그 후에 CompositionCatalog에 추가하면 된다.

  이제, Names에 내용물들이 채워졌을 것이다. ListBox에 정렬시켜보자.

NameListBox.ItemsSource = Cities;

  이제 -ㅁ-) 실행시켜 보면, ListBox에 Names에 있던 요소들이 모두 출력되어 있는 것을 확인할 수 있을 것이다.
  여기서 끗!

... 이라고 하고 싶지만, 내가 원하는 형태는 애니메이션이 있는 UserControl을 Export시키고, .xap를 로드하여 UserControl을 Import로 정보를 얻어  애니메이션을 볼 수 있는 것이다. 이에 대해서는, 다음에 포스팅하도록 하겠다.
Posted by 알 수 없는 사용자
Tech/Silverlight2010. 4. 21. 20:28


Silverlight 함수 중에서 Dictionary를 매개변수로 받는 함수 호출 하는 법에 대해 글을 쓰겠습니다.

우선 Silverlight 응용 프로젝트를 생성하신 후,
간단하게 생성자에서


HtmlPage.RegisterScriptableObject("slInternal", this);
 
을 호출합니다.
HtmlPage.RegisterScriptableObject이 하는 역할은 JavaScript 코드의 스크립트 가능한 액세스 기능을 위해 관리되는 개체를 등록합니다.
즉, slInternal이라는 이름으로 this라는 객체를 JavaScript 코드에서 접근 가능하도록 등록해준다는 말입니다.
 
그리고, 이제 JavaScript에서 호출한 함수를 작성해줍니다.
 
1
2
3
4
5
6
7
8
[ScriptableMember]
public void ShowDictionaryPair(Dictionary<string, int> stringDict)
   {
       foreach (var pair in stringDict)
       {
            MessageBox.Show(pair.ToString());
       }
   }
JavaScript에서 호출할 함수를 작성할 때에는 꼭 [ScriptableMember]를 함수 정의 전에 써 줍니다.
저는 Dictionary를 매개변수로 받아서 foreach문을 돌면서 Dictionary에 있는 Key와 Value를 MessageBox를 통해 보이도록 함수를 만들었습니다.

자 이제, JavaScript가 있는 곳으로 이동해 볼까요.
.Web 프로젝트의 속성에 들어가서 웹 태그에 특정 페이지로 설정되어진 .html이나 .aspx파일을 확인한 후,
그 페이지로 들어가서 작업하시면 됩니다.

우선 하단에 있는 Body 태그를 확인하시고, Object 태그에 id 속성을 추가하여 정의해주세요.
바로 요렇게 말입니다.

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%" id="silverlightControl">
저는 silverlightControl이라고 정의하였습니다.
그리고 Load가 되면 silverlight에서 정의한 함수를 호출하도록 하겠습니다.
Param 태그에 이벤트 함수를 추가해주세요.
바로 요렇게 말입니다.
 
<param name="onLoad" value="onSilverlightLoad" />
 
그 후에, 상단으로 올라가셔서 대략 onSilverlightError이벤트 함수가 정의된 곳에서 onSilverlightLoad이벤트 함수를 추가해주시면 됩니다.
 
1
2
3
4
5
6
function onSilverlightLoad(sender, args) {
    var slObj = document.getElementById("silverlightControl");
        var dict = slObj.content.slInternal.createManagedObject("Dictionary<string, int>");
        dict["Color"] = 1;
        slObj.content.slInternal.SayHelloToMe(dict);
    }
slObj에 아까 object 태그에 id로 정의했던 silverlightControl을 getElementById함수를 이용하여 호출한 뒤,
createManagedObject함수를 이용하여 Dictionary를 생성해주세요.

주의할 점은 Javascript에서 생성할 수 있는 Dictionary는 항상 Key의 Type이 string이여야 합니다.

그 후에, 생성한 Dictionary 객체에 원하는 아이템을 설정한 후,
silverlight에서 정의했던 함수를 호출해주시면 됩니다.

그러면 이제, 디버깅을 해보면!




Good Job!!

'Tech > Silverlight' 카테고리의 다른 글

[Silverlight MEF - Export UserControl]  (2) 2010.06.02
[Silverlight MEF - Dynamic XAP Loading]  (1) 2010.05.12
[Silverlight Custom Font 적용]  (0) 2010.01.12
Custom Animation in Silverlight  (1) 2010.01.06
Posted by 알 수 없는 사용자
Tech/Silverlight2010. 1. 12. 15:32

매우 오랜만에 글을 쓰네요 창피창피...

그다지 Font에 관해서 신경을 쓰지 않고 넘어갔었는데, 이번에 디자이너 분들에게 요청을 받은 것이 기회가 되어 Custom Font 적용 방법을 찾아볼 수 있게 되었던 것 같아요.

우선 블렌드에서 Custom Font 적용하는 방법을 알아보도록 하죠.


1. 프로젝트를 생성하고 LayoutRoot의 Background 색상을 지정하고 TextBlock을 추가하였습니다.



2. TextBlock의 FontFamily를 Silverlight 에서 지원하지 않는 폰트로 지정해보겠습니다. 저는 Bradley Hand ITC 폰트를 선택하였습니다.



3. 폰트를 지정한 후, 텍스트 속성에서 몇 가지 작업을 수행해야 합니다.
- 포함을 체크합니다.
- 아니면,글꼴 관리자를 눌러 응용프로그램에 포함할 글꼴로 선택을 해줍니다.




4.
이후에 프로젝트 폴더를 확인해보면 Fonts 폴더가 생성된 것을 볼 수 있을 것입니다.



5. .XAML 파일에 생성된 코드도 확인해보죠 ^-^


<TextBlock Text="Good Day" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48" FontFamily="Fonts/Fonts.zip#Bradley Hand ITC"/>

다른 건 다 필요없고 FontFamily="Fonts/Fonts.zip#Bradley Hand ITC" 요 부분에 집중해 주세요!
생성된 Fonts 폴더 아래에 압축된 Fonts.zip에 있는 폰트 정보를 설정해주고 있습니다.

으잉? Fonts.zip?
갑자기 왠 .zip 파일일까요.

탐색기로 들어가 직접 프로젝트 폴더를 열어보도록 하죠.



프로젝트 하위 폴더의 Fonts 폴더가 생성되어 있습니다. 하지만 저 폴더를 열어도 Fonts.zip 파일은 없습니다.
obj → Debug 폴더로 경로를 이동하면,



여기에도 Fonts 폴더가 있네요. 한 번 열어볼까요?



옹, 여기에 있었군요. 이 .zip 파일을 열어보면 폰트가 들어있는 것을 확인할 수 있답니다.

자, Custom Font를 적용하였을 경우, 생성되는 결과물들을 확인하였고...
이제 프로젝트를 빌드시킨 후, 실행시켰을 때 본인이 원하는 폰트가 적용되었음을 확인할 수 있을 것입니다.

음... 그런데 한 가지 문제가 더 있습니다.
한글 폰트를 이 방법으로 적용시키려 할 경우, 적용이 쉽게 되지 않음을 알 수 있으실 거에요.

다음과 같이 나눔명조 폰트를 적용시켰을 경우, 생성되는 .XAML 코드는 똑같습니다.

<TextBlock Text="안녕하세요" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48" FontFamily="Fonts/Fonts.zip#나눔명조"/>

하지만 프로젝트를 빌드한 후, 실행을 시켜봐도 본인이 원하는 폰트가 적용되지 않았음을 확인하게 됩니다.



이 문제의 원인은 FontFamily에 폰트를 적용시킬 때, 한글 이름을 주어서 발생하는 문제입니다.
때문에 해당 폰트의 속성으로 들어가서 자세히 탭에서 영어 이름을 알아내어 적용시켜 주시면 됩니다.



나눔명조의 영어 이름을 알아내었으니 실제 .XAML 코드에 적용시켜 보도록 하죠!

<TextBlock Text="안녕하세요" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48" FontFamily="Fonts/Fonts.zip#NanumMyeongjo"/>

자~ 이제 빌드하고, 실행시켜보면...



예쁘게 나눔명조 폰트가 적용되었습니다! ^-^)/

이렇게 해당 폰트의 속성창으로 들어가서 영어 이름을 확인하시는 방법도 있지만, 공도씨의 블로그에 가 보시면 폰트의 영어 이름을 검색하여 쉽게 알아낼 수 있는 프로그램이 공개되어 있습니다.
그 프로그램을 다운받아 검색란에 폰트의 이름을 검색하면 영어 이름을 보다 간편하게 알아낼 수 있습니다.
검색 기준 폴더는 윈도우즈 폴더의 Fonts 폴더같고요.

이상 Blend 에서 Custom Font 적용 방법에 대한 글을 마치도록 하겠습니다.

아쟈아쟈아쟈!
Posted by 알 수 없는 사용자
Tech/Silverlight2010. 1. 6. 17:11

1. Custom Animation?

Silverlight에서 Storyboard를 사용한 Animation을 적용하고자 할 때 특정한 Property에 대한 Animation 적용이 힘든 경우가 있다. (ex. Grid의 Margin)

그것을 위해서 사용자가 직접 Animation을 구현하는 Class를 작성하면 된다.

Custom Animation Class는 DependencyProperty로 Time, Target, From, To, TargetProperty를 구현한다.


2. Thickness Animation

Grid의 Margin은 Type이 Thickness이다. 이것의 Animation을 위한 ThicknessAnimation Class는 다음과 같이 구현할 수 있다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

using System.Windows;
using System.Windows.Media.Animation;

namespace CCPContentsList
{
    public class ThicknessAnimation  
    { 
        // The time along the animation from 0-1
        public static DependencyProperty TimeProperty = DependencyProperty.RegisterAttached(
            "Time", typeof(double), typeof(DoubleAnimation), new PropertyMetadata(OnTimeChanged));
        // The object being animated
        public static DependencyProperty TargetProperty = DependencyProperty.RegisterAttached(
            "Target", typeof(DependencyObject), typeof(ThicknessAnimation), null);
        // The thickness we're animating to
        public static DependencyProperty FromProperty = DependencyProperty.RegisterAttached(
            "From", typeof(Thickness), typeof(DependencyObject), null);
        // The tickness we're animating from
        public static DependencyProperty ToProperty = DependencyProperty.RegisterAttached(
            "To", typeof(Thickness), typeof(DependencyObject), null);
        // The target property to animate to.  Should have a property type of Thickness
        public static DependencyProperty TargetPropertyProperty = DependencyProperty.RegisterAttached(
            "TargetProperty", typeof(DependencyProperty), typeof(DependencyObject), null);

        /// <summary>
        /// Creates a Timeline used to animate the thickness of an object
        /// </summary>
        /// <param name="target">The object to animate</param>
        /// <param name="targetProperty">The property on the object to animate</param>
        /// <param name="duration">The length of the animation</param>
        /// <param name="from">The begining thickness</param>
        /// <param name="to">The final thickness</param>
        /// <returns>A timeline object that can be added to a storyboard</returns>
         public static Timeline Create(
             DependencyObject target, 
             DependencyProperty targetProperty, 
             Duration duration, 
             Thickness from, 
             Thickness to, 
             EasingFunctionBase ease)
         {
             DoubleAnimation timeAnimation = new DoubleAnimation() 
                    { From = 0, To = 1, Duration = duration, EasingFunction = ease };
             timeAnimation.SetValue(TargetProperty, target);
             timeAnimation.SetValue(TargetPropertyProperty, targetProperty);
             timeAnimation.SetValue(FromProperty, from);
             timeAnimation.SetValue(ToProperty, to);
             Storyboard.SetTargetProperty(timeAnimation, new PropertyPath("(ThicknessAnimation.Time)"));
             Storyboard.SetTarget(timeAnimation, timeAnimation); 
             return timeAnimation;
         }

         ///<summary>
         ///Silverlight's animation system is animating time from 0 to 1.  
         ///When time changes we update the thickness to be time
         ///percent between from and to 
         ///</summary>
         private static void OnTimeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
         {
             DoubleAnimation animation = (DoubleAnimation)sender;
             double time = GetTime(animation);
             Thickness from = (Thickness)sender.GetValue(FromProperty);
             Thickness to = (Thickness)sender.GetValue(ToProperty);
             DependencyProperty targetProperty = (DependencyProperty)sender.GetValue(TargetPropertyProperty);
             DependencyObject target = (DependencyObject)sender.GetValue(TargetProperty);
             target.SetValue(targetProperty, new Thickness((to.Left - from.Left) * time + from.Left,
             (to.Top - from.Top) * time + from.Top,
             (to.Right - from.Right) * time + from.Right,
             (to.Bottom - from.Bottom) * time + from.Bottom));
         } 

         public static double GetTime(DoubleAnimation animation)
         {
            return (double)animation.GetValue(TimeProperty);
         }

         public static void SetTime(DoubleAnimation animation, double value)
         {
            animation.SetValue(TimeProperty, value);
         }
    }
}

- 여기서 중요한 부분은 OnTimeChanged 함수이다. 이 함수로 들어온 DoubleAnimation으로 부터 설정한 애니메이션 정보를 얻은 다음에, Margin 값을 직접 수정하게 된다. (Margin의 Type은 Thickness)

- 이를 응용하면 애니메이션 적용이 힘든 다른 Property도 Custom Animation을 제작해 사용할 수 있다.


Posted by 알 수 없는 사용자
Tech/Silverlight2010. 1. 1. 14:21

Commands in Silverlight with SLExtensions - 1에서 이어집니다.

일단, 앞에서 새로 빌드한 SLExtensions.dll 파일을 프로젝트 참조에 추가해두고 다음으로 넘어가도록 합시다.

커맨드 제공자

첫 번째로는 커맨드들을 모아 둘 Commands 라는 정적 클래스를 만듭니다. 가령 무엇인가 삭제하는 Delete 라는 커맨드를 만든다고 하면 다음과 같은 Commands 클래스가 만들어지게 됩니다.

public static class Commands
{
public static Command DeleteCommand { get; private set; }

static Commands()
{
DeleteCommand = new Command("DeleteCommandName");
}
}

정적 생성자에서 정적 속성인 DeleteCommand를 초기화하는 것을 볼 수 있습니다. Command 개체의 생성자에 넣는 커맨드 이름은, 사용하려는 커맨드 이름과 동일하게 "DeleteCommand"라고 지정하는 것이 보통이겠으나 여기서는 확실히 구분을 주기 위해서 "DeleteCommandName"이라고 지정해 보았습니다.


ViewModel 연결

ViewModel 에서는 삭제 커맨드가 실행되었을 때 뭔가 삭제하는 작업을 수행해주어야 되겠죠. 커맨드가 실행되었다는 것을 알기 위해서 다음과 같이 생성자에서 Executed 이벤트를 구독합니다.

public MainPageViewModel()
{
Commands.DeleteCommand.Executed += new EventHandler<ExecutedEventArgs>(DeleteCommand_Executed);
}

DeleteCommand_Executed 라는 메서드에서 삭제 작업을 수행해 줄 것이라고 기대할 수 있겠습니다.


View 연결

이제 View에서 커맨드를 발생시키도록 합니다. 먼저 커맨드 관련 클래스들이 포함되어 있는 SLExtensions.Input 네임스페이스를 추가합니다.

xmlns:input="clr-namespace:SLExtensions.Input;assembly=SLExtensions"

다음으로는 삭제 버튼에 삭제 커맨드를 지정해줍니다.

<Button Content="Delete" input:CommandService.Command="DeleteCommandName"/>

DeleteCommandName 이라는 문자열을 사용해서 실행될 커맨드를 지정하고 있습니다. 이제 이 버튼을 클릭하면 삭제 커맨드가 실행되는 것이지요.


View - ViewModel 연결

한 번 실행해보기 위한 작업을 해 봅시다. 일단 ViewModel의 DeleteCommand_Executed 메서드에서 메시지 박스를 띄우도록 합니다.

void DeleteCommand_Executed(object sender, ExecutedEventArgs e)
{
MessageBox.Show("DeleteCommand");
}

그리고 View 생성 시 ViewModel이 같이 생성되도록 View의 xaml 파일을 수정합니다. 일단 프로젝트의 네임스페이스를 추가하고,

xmlns:local="clr-namespace:SilverlightApplication3"

ViewModel을 생성하도록 데이터 컨텍스트를 추가합니다. UserControl 태그의 여는 태그 바로 아래에 다음과 같은 내용을 추가합니다.

<UserControl.DataContext>
<
local:MainPageViewModel />
</
UserControl.DataContext>

이제 프로젝트를 실행해서 삭제 버튼을 누르면 메시지 박스가 뜨는 것을 볼 수 있을 것입니다.


이번에는 커맨드 패턴을 이용하여 어떻게 View와 ViewModel을 연결시키는지 간단히 살펴보았습니다. 다음 연재에서 좀 더 상세한 내용을 다뤄보도록 하겠습니다.

'Tech > Silverlight' 카테고리의 다른 글

[Silverlight Custom Font 적용]  (0) 2010.01.12
Custom Animation in Silverlight  (1) 2010.01.06
Commands in Silverlight with SLExtensions - 1  (0) 2009.12.26
Silverlight Pixel Shader 개발 팁  (0) 2009.10.28
Posted by wafe
Tech/Silverlight2009. 12. 26. 20:40

커맨드 패턴이 뭔지에 대한 설명은 생략하고 다짜고짜 들어가보도록 하겠습니다.

왜 커맨드 패턴인가?

보통 WPF/Silverlight에서는 Presentation Model 패턴(흔히 MVVM 패턴)을 써서 GUI(View)와 프로그램 상태(ViewModel)를 분리합니다. Binding 이라는 기술적인 개념 덕분에 xaml.cs 파일에는 코드 한 줄 쓰지 않고도 View와 ViewModel을 연결할 수가 있고, View와 ViewModel이 서로를 몰라도 되는 상황을 유지할 수 있습니다. 그런데 이건 보여주는 기능에만 해당되는 얘기일 뿐 사용자가 버튼을 클릭했을 때 발생하는 이벤트를 처리한다거나 하는 일을 위해서는 xaml.cs(View)에서 코드를 통해 ViewModel의 메서드를 호출해 줘야 합니다.

커맨드 패턴을 사용하면 View와 ViewModel의 분리 상태를 유지하면서 View에서 발생한 이벤트를 ViewModel로 알려줄 수 있게 됩니다.

준비 사항

Silverlight 4 이전 버전에서 커맨드 패턴을 사용하려면 SLExtensionsComposite Client Application Guidance(Prism) 라이브러리를 사용하면 됩니다. MS의 patterns & practices 그룹에서 발표한 Prism을 쓰고 싶었지만 단지 커맨드 패턴만을 쓰기 위해서는 프레임워크도 그렇고 문서도 그렇고 너무 방대해서 짧은 시간에 파악하기 힘들었습니다. 그래서 이번에는 SLExtensions를 사용해보도록 하겠습니다.

SLExtensions 다운로드 페이지에서 받을 수 있는 알파 릴리즈를 사용하면 Blend에서 프로젝트를 열였을 때 다음과 같은 에러가 발생하면서 XAML을 로드할 수 없게 됩니다.

A command with the same name '{0}' is already registered.

Blend에서 더미 데이터가 채워져 있는 GUI를 보면서 디자인 할 수 있다는 건 상당한 장점인데, 그러한 장점을 못쓰게 된다는 것은 뼈아픈 일이지요. 다행히 최신 소스 코드(현재 Changeset 35533)에서는 이 문제가 해결되어 있습니다.

일단 Source Code 페이지 우측의 Latest Version Download 링크에서 최신 버전 소스를 받습니다. 압축을 풀면 나오는 소스에서 trunk/SLExtensions/SLExtensions 폴더의 프로젝트를 빌드해서 사용하면 됩니다. 빌드된 dll 파일은 ClientBin 폴더에 생성됩니다.


다음 연재 : Commands in Silverlight with SLExtensions - 2

'Tech > Silverlight' 카테고리의 다른 글

Custom Animation in Silverlight  (1) 2010.01.06
Commands in Silverlight with SLExtensions - 2  (0) 2010.01.01
Silverlight Pixel Shader 개발 팁  (0) 2009.10.28
[Element Binding]  (0) 2009.10.12
Posted by wafe