IsolatedStorage를 사용하면 Blend에서 디자인이 안보인다?에서 DesignerProperties 클래스를 활용하여 Blend에서 실행중인지 여부를 구분할 수 있다고 했는데요, DesignerProperties를 보다 정확하게 활용할 수 있는 간단한 방법을 소개합니다.

기본적으로 DesignerProperties는 다음과 같이 사용하면 되죠.

if (DesignersProperties.GetIsInDesignMode(AnyUIElementInVisualTree) == true)
    // 디자인 모드
else
    // Web 모드

간단하죠? 그런데 한 가지 문제는 반드시 인자로 VisualTree에 올라가 있는 UIElement를 필요로 한다는 점이에요. 보통 UserControl이나 Control 안에 코드를 작성할 때에는 단순히 GetIsDesignerMode(this)를 해도 무방하지만 Model과 같이 따로 UIElement를 확정할 수 없는 코드에서는 난감하죠. 이럴 때에는 다음과 같이 해결할 수 있어요.

if (DesignersProperties.GetIsInDesignMode(Application.Current.RootVisual) == true)
    // 디자인 모드
else
    // Web 모드

Application이 지원하는 RootVisual은 전역에서 일관적으로 접근할 수 있는데다가 비주얼 트리VisualTree의 최상위 오브젝트를 의미하니까 완벽해 보여요. 그렇지만 여기에도 문제는 있는데요, 바로 Application이 아직 RootVisual을 결정하지 못한 상태일 때에 이 검사는 실패한다는 점이죠. 일반적인 실버라이트 애플리케이션의 App.xaml.cs를 보죠.

public App()
{
    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    InitializeComponent();
}

private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new Page();
}

7행의 InitializeComponent();가 실행되기 직전에는 Application.Current.RootVisual이 실버라이트 애플리케이션을 다운로드 받는 동안 보이는 로딩 화면Splash에 대한 비주얼 트리를 담고 있죠.

image

그 후 애플리케이션이 초기화 되고 로딩 화면이 사라진 후 Application_Startup 이벤트 핸들러가 호출되는데요, 12행의 RootVisual을 new Page로 설정하기 직전까지는 Application.Current.RootVisual이 null 값을 가지고 있어요.

즉, 애플리케이션이 초기화 된 직후부터 RootVisual을 설정하기 전까지 Application.Current.RootVisual이 null이므로 이 사이에는 DesignerProperties.GetIsDesignMode(Application.Current.RootVisual)이란 코드는 안전하지 않게 되는거죠. 한 가지 다행인 점은 앞서 본 것 처럼 App 클래스의 생성자에서 RootVisual이 null이 아니기 때문에 이를 이용하여 다음과 같이 안전한 코드를 작성할 수 있어요.

// 초기값은 블렌드에서 실행중이라고 가정합니다.
private bool _isBlend = true;
/// 
/// 블렌드에서 실행중인지 여부를 반환합니다.
/// 
public bool IsBlend
{
    get { return _isBlend; }
    private set { _isBlend = value; }
}

public App()
{
    // 블렌드에서 실행중인지 여부를 검사합니다.
    IsBlend = DesignerProperties.GetIsInDesignMode(RootVisual);

    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    InitializeComponent();
}

여기서 중요한 점은 최초에는 블렌드에서 실행중이라고 가정한다는 거에요. 그 이유는 내부적으로 블렌드에서 실행될 때에 분명히 Application의 생성자가 호출되지만 우리가 그것을 확인할 수 없기 떄문에 실행 코드의 ‘모호성’을 줄이기 위해서에요. 반면 웹 페이지로 동작하고 있다면 이 코드는 100% 동작한다고 확신할 수 있으니 초기 값을 안전하기 가져가는 것이 좋겠죠.

그리고 코드를 애플리케이션 전역에서 공유할 수 있도록 App 클래스의 static 멤버로 정의했기 때문에 애플리케이션의 어떤 코드에서도 안전하게 가져갈 수 있겠죠.

만약 라이브러리에서도 블렌드에서 실행중인지 여부를 검사해야 한다면 여기에서 한 단계 더 나아가 위의 코드를 래핑하는 전역 클래스Static Class를 하나 만들 수도 있고요. 이 부분은 각자 프로젝트에 맞춰서 해보세요.

저작자 표시 동일 조건 변경 허락
신고
Posted by gongdo

컴퍼넌트 및 컨트롤 개발자/디자이너에게 반드시 필요한 기능이죠. 보통 우리가 컨트롤을 디자인 할 때에는 특히, 데이터 바인딩을 사용하는 컨트롤의 경우에는 실제 데이터가 표시될 자리에는 아직 아무런 데이터가 들어가지 않았기 때문에 디자이너들이 그 곳에 임의의 값이 어떤 형태로 들어갈지 알 수 없다는 문제점이 있죠. 바로 이럴 때 System.ComponentModel.DesignerProperties 클래스를 유용하게 사용할 수 있어요.

우선 샘플 프로젝트부터 받아 보시죠.

먼저 간단하게 유저 컨트롤을 하나 만들어서 테스트 해 보죠. 컨트롤의 이름은 MyUserControl이라고 하고 크기는 100x100으로 아무것도 없는 컨트롤이에요. 그리고 코드 비하인드에 다음과 같이 간단한 코드를 넣어보죠.

public MyUserControl()
{
    InitializeComponent();
    TextBlock tb = new TextBlock();

    if (DesignerProperties.GetIsInDesignMode(this) == true)
    {
        // Design-time
        LayoutRoot.Background = new SolidColorBrush(Colors.Blue);
        tb.Text = "in Design";
    }
    else
    {
        // Run-time
        LayoutRoot.Background = new SolidColorBrush(Colors.Green);
        tb.Text = "in Runtime";
    }

    LayoutRoot.Children.Add(tb);
}

DesignerProperties.GetIsInDesignMode() 메서드는 주어진 UIElement가 현재 디자인 모드에서 렌더링 되고 있는지 여부를 반환해요. 해당 UIElement가 디자인 모드 즉, Blend에서 렌더링 되고 있다면 true를, 웹브라우저 등에서 렌더링 되고 있다면 false를 반환하는거죠.

위의 코드는 컨트롤이 인스턴스화 될 때 디자인 모드에서는 파란색으로 컨트롤을 채우고 "in Design"이란 문자열을 출력하고 런타임에서는 녹색으로 컨트롤을 채우고 "in Runtime"이란 문자열을 출력하도록 작성되었어요.

비주얼 스튜디오에서 MyUserControl을 볼까요?

아무것도 안나오죠? 이것은 비주얼 스튜디오의 XAML 뷰어(코드명 사이더)는 해당 UserControl을 새로 생성하는 것이 아니라 단지 XAML을 분석하여 보여준다는 것을 알 수 있죠.

블렌드의 경우도 마찬가지에요.

 

자, 다음으로 MyUserControl을 XAML에서 추가하여 사용하는 Page.xaml을 한번 볼까요?

비주얼 스튜디오의 XAML 뷰어에서는 디자인 모드가 아닌 런타임으로 판단하는군요. 이것은 아마도 버그내지는 잘못된 판단이 아닐까 싶어요. 아무리 사이더에서 실제로 디자인 하는게 불가능하더라도 엄연히 이 시점은 '디자인'상태니까요.

반면, 블렌드에서는 디자인 모드를 정상적으로 인식하는 것을 볼 수 있죠.

그렇다면 브라우저에서는? 당연히 런타임으로 나오겠죠?

 

DesignerProperties 클래스가 유용하게 쓰이는 경우는 앞에서 말했던 것처럼 동적으로 변경되는 값의 샘플 값을 디자이너에게 보여주고 싶을 때인데요 이 외에도 이 클래스를 반드시 써야 하는 경우도 있어요. 바로 몇몇 코드는 블렌드에서 처리할 수 없어서 에러를 발생시키는 원인이 되기도 해요.

이렇게 말이죠. 이게 당해보면 진짜 환장할 노릇이거든요. 왜냐면 유저 컨트롤 그 자체에서는 잘 보이는데 그 컨트롤을 사용한 다른 컨트롤의 디자인에서만 안보이니까요.

어떤 경우에 위와 같은 에러가 나오는지 완전히 정리되지는 않았지만 한가지 확실한 것은 IsolatedStorage가 사용되었을 때에요. IsolatedStorage를 쓸 때의 주의점과 해결 방법은 다음 포스팅에서 설명할께요.

저작자 표시 동일 조건 변경 허락
신고
Posted by gongdo

실버라이트 2 RTW는 최종 사용자를 위해 디버깅 관련 어셈블리들이 제거된 약 4.6메가 정도의 런타임이 배포 되죠.
하지만 개발자용 런타임은 이런저런 어셈블리들이 더 추가되어 있어서 약 6.5메가 정도로 배포 되고요.

만약 개발자가 개발자용 런타임을 설치하지 않고 일반 사용자용 런타임만을 설치한 경우 비주얼 스튜디오에서 디버깅으로 실행할 때 다음과 같은 메시지를 만나게 되죠.

Unable to start debugging. The Silverlight managed debugging package isn't installed.

그럼 개발자용 런타임은 어디있냐고요?

기본적으로 Silverlight 2 Tools for Visual Studio(Silverlight_Tools.exe)를 설치하면 SDK와 다른 패치들과 함께 개발자용 런타임이 설치되죠. 그러나 이 과정은 정말로 오래 걸리므로 그냥 개발자용 런타임만 따로 얻으려면 다음의 링크에서 받으면 돼요.

http://go.microsoft.com/fwlink/?LinkId=127693

그런데 위의 링크는 Silverlight_Tools.exe의 압축을 푼 후 그 안에 있는 ParameterInfo.xml 파일을 열어서 얻을 수 있었는데요, 구글에서 Silverlight 2 Developer Runtime으로 검색하면 위와는 다른 링크를 던져주는군요.

http://go.microsoft.com/fwlink/?LinkId=119972

어느게 맞는 건지는 모르겠는데 아무래도 위쪽의 링크가 더 정확하겠죠?
정확히 아시는 분 제보를 바랍니다. :)

P.S.
런타임 다시 깔 때 꼭 Visual Studio를 닫아 두세요. 가끔 띄워 놓은 상태에서 런타임 깔다가 비주얼 스튜디오가 죽을 때가 있어요.


저작자 표시 동일 조건 변경 허락
신고
Posted by gongdo


티스토리 툴바