WPF Designer 로드 실패 문제 해결
업데이트: 2007년 11월
Windows Presentation Foundation(WPF) Designer for Visual Studio에는 XAML을 렌더링하는 정교하며 확장 가능한 시각적 디자이너가 포함되어 있습니다. 디자이너에서 XAML 파일이 로드되지 않는 경우 몇 가지 방법을 통해 무엇이 문제인지 알아낼 수 있습니다. 이 항목에서는 WPF Designer 로드 실패 문제를 해결하는 데 도움이 되는 몇 가지 팁과 방법에 대해 설명합니다.
참고: |
---|
이 항목에서 설명하는 대부분의 방법은 Expression Blend에도 적용됩니다. |
문제 해결 단계
다음 단계를 수행하면 WPF Designer 로드 실패 문제를 해결하는 데 도움이 됩니다.
표시된 예외 메시지가 있으면 자세히 읽어 보십시오.
당연한 것처럼 보일 수도 있지만 예외 메시지가 표시되면 메시지의 내용을 명확하게 이해해야 합니다. 간혹 이렇게 하면 문제를 신속하게 진단하는 데 도움이 됩니다. 자세한 내용은 WPF Designer의 오류 디버깅 및 해석을 참조하십시오.
문제가 구현 내부에 있는지 확인하십시오.
응용 프로그램을 빌드하고 실행하여 문제가 구현으로 인한 것인지 아니면 WPF Designer와의 상호 작용으로 인한 것인지 확인합니다. 응용 프로그램이 빌드되고 실행되면 구현으로 인해 디자인 타임 오류가 발생했을 수 있습니다.
Visual Studio 디버거를 사용하여 디자인 타임에 코드를 한 단계씩 실행하십시오. 자세한 내용은 연습: 디자인 타임에 WPF 사용자 지정 컨트롤 디버깅을 참조하십시오.
로드 오류로 인한 문제인지 확인하십시오.
예외로 인해 디자인 뷰가 로드되지 않으면 로드 오류로 인해 문제가 발생했을 수 있습니다. 디자인 타임에 로드된 사용자 지정 코드가 있고 디자인 타임에 예외 또는 로드 실패가 발생할 경우 이 항목의 디자인 타임용 코드 작성 단원을 참조하십시오. 리소스로 작업 중인데 리소스가 로드되는 것 같지 않으면 이 항목의 디자인 타임의 UserControl 및 사용자 지정 컨트롤 리소스 단원을 참조하십시오.
디자인 타임에 로드된 코드를 검토하십시오.
디자인 타임에도 실행되는 코드를 작성하는 방법은 두 가지입니다. 첫 번째 방법은 클래스에 대한 입력 매개 변수를 검사하여 안전한 코드를 작성하는 것입니다. 두 번째 방법은 GetIsInDesignMode 메서드를 호출하여 디자인 모드가 활성 상태인지 여부를 확인하는 것입니다. 자세한 내용은 이 항목의 디자인 타임용 코드 작성 단원을 참조하십시오.
코드의 다른 부분을 검토하십시오.
WPF Designer를 사용할 때 도움이 되는 몇 가지 프로그래밍 팁을 보려면 이 항목의 프로그래밍 팁 단원을 참조하십시오. 보다 강력한 코드를 작성하는 방법은 이 항목의 최선의 프로그래밍 방법 단원을 참조하십시오.
그래도 문제가 해결되지 않으면 WPF Designer forum on MSDN에서 WPF Designer를 사용하는 다른 개발자와 정보를 교환하십시오. 잠재적 문제를 보고하거나 의견을 제안하려면 Visual Studio and .NET Framework 피드백 사이트를 이용하십시오.
디자인 타임용 코드 작성
코드는 런타임과 디자인 타임에 모두 실행되어야 합니다. 코드가 디자인 타임에 실행되더라도 Application.Current가 사용자 응용 프로그램이라고 단정하지 마십시오. 예를 들어, Expression Blend를 사용할 경우 Current는 Expression Blend입니다. 디자인 타임에는 MainWindow가 사용자 응용 프로그램의 주 창이 아닙니다. 사용자 지정 컨트롤이 디자인 타임에 로드되지 않게 하는 일반적인 작업에는 다음이 포함됩니다.
Current를 Application의 사용자 지정 서브클래스로 캐스팅
MainWindow를 Window의 사용자 지정 서브클래스로 캐스팅
Current 또는 MainWindow에 FindResource 또는 FindName 메서드 사용
Application.Resources의 리소스 참조. Application의 디자인 타임 인스턴스는 사용자의 응용 프로그램과 동일하지 않으며 따라서 동일한 리소스를 사용하지 않습니다.
Application.Current 또는 Application.MainWindow가 null 값을 반환했는지 여부를 확인 안 함. Visual Studio에서 응용 프로그램 개체를 만들지 않으면 Application.Current가 null을 반환합니다.
Assembly.GetEntryAssembly가 null 값을 반환했는지 여부를 확인 안 함. Visual Studio에서는 이 메서드가 null을 반환합니다.
디자인 타임용 코드를 작성하는 방법은 두 가지입니다. 첫 번째 방법은 값 변환기와 같이 클래스에 대한 입력 매개 변수를 검사하여 안전한 코드를 작성하는 것입니다. 두 번째 방법은 GetIsInDesignMode 메서드를 호출하여 디자인 모드가 활성 상태인지 여부를 확인하는 것입니다.
디자인 환경은 일부 입력에 대해 런타임 환경보다 다양한 형식을 제공하므로 일부 구현의 경우 입력 매개 변수 검사가 반드시 필요합니다.
스타일 선택기와 값 변환기의 경우 디자인 타임에 올바르게 실행되려면 이 두 방법 중 하나를 사용해야 합니다.
값 변환기
사용자 지정 IValueConverter 구현은 null이 있는지, 그리고 Convert 메서드의 첫 번째 매개 변수에 필요한 형식이 있는지 검사해야 합니다. 다음 XAML은 값 변환기가 올바르게 구현되지 않은 경우 디자인 타임에 실패하는 Application.Current에 대한 바인딩을 보여 줍니다.
<ComboBox.IsEnabled>
<MultiBinding Converter="{StaticResource specialFeaturesConverter}">
<Binding Path="CurrentUser.Rating" Source="{x:Static Application.Current}"/>
<Binding Path="CurrentUser.MemberSince" Source="{x:Static Application.Current}"/>
</MultiBinding>
</ComboBox.IsEnabled>
Application.Current가 사용자의 응용 프로그램이 아니라 디자이너 응용 프로그램을 참조하므로 이 바인딩은 디자인 타임에 예외를 발생시킵니다. 예외가 발생하지 않도록 하려면 값 변환기가 입력 매개 변수 또는 디자인 모드를 검사해야 합니다.
다음 코드 예제에서는 두 입력 매개 변수가 특정 비즈니스 논리를 만족할 경우 true를 반환하도록 값 변환기를 통해 입력 매개 변수를 검사하는 방법을 보여 줍니다.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Check the values array for correct parameters.
// Designers may send null or unexpected values.
if (values == null || values.Length < 2) return false;
if (!(values[0] is int)) return false;
if (!(values[1] is DateTime)) return false;
int rating = (int)values[0];
DateTime date = (DateTime)values[1];
// If the user has a good rating (10+) and has been a member for
// more than a year, special features are available.
if((rating >= 10) &&
(date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0))))
{
return true;
}
return false;
}
두 번째 방법은 디자인 모드가 활성 상태인지 여부를 확인하기 위해 디자인 타임용 코드를 작성하는 것입니다. 다음 코드 예제에서는 앞에서 보여 준 매개 변수 검사 대신 디자인 모드 검사를 보여 줍니다.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Check for design mode.
if ((bool)(DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue))
{
return false;
}
int rating = (int)values[0];
DateTime date = (DateTime)values[1];
// If the user has a good rating (10+) and has been a member for
// more than a year, special features are available.
if((rating >= 10) &&
(date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0))))
{
return true;
}
return false;
}
스타일 선택기
사용자 지정 스타일 선택기도 디자인 모드에서 실행되도록 구현해야 합니다. 다음 XAML은 DataTemplate으로 반환되는 리소스를 결정하기 위해 런타임에 Application.MainWindow를 사용하는 사용자 지정 템플릿 선택기를 보여 줍니다. 디자인 타임에 이 리소스를 사용할 수 없을 수도 있으므로 SelectTemplate 재정의가 디자인 타임에 null을 반환합니다.
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"
IsSynchronizedWithCurrentItem="True"/>
다음 코드에서는 스타일 선택기의 구현을 보여 줍니다.
public class TaskListDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(
object item,
DependencyObject container)
{
if (item != null && item is Task)
{
Task taskitem = item as Task;
Window window = Application.Current.MainWindow;
// To run in design mode, either test for the correct window class
// or test for design mode.
if (window.GetType() == typeof(Window1))
// Or check for design mode:
//if (!DesignerProperties.GetIsInDesignMode(window))
{
if (taskitem.Priority == 1)
return window.FindResource("importantTaskTemplate") as DataTemplate;
else
return window.FindResource("myTaskTemplate") as DataTemplate;
}
}
return null;
}
}
디자인 타임의 UserControl 및 사용자 지정 컨트롤 리소스
기본적으로 런타임에 사용할 수 있는 UserControl 및 사용자 지정 컨트롤 리소스를 디자인 타임에 사용할 수 없을 수도 있습니다. 사용자 지정 컨트롤과 사용자 정의 컨트롤을 디자인 화면의 Page 또는 Window에 추가하면 컨트롤의 인스턴스가 만들어집니다. App.xaml의 리소스는 UserControl에 사용할 수 없으므로 사용자 지정 컨트롤 인스턴스가 페이지 또는 창에 로드됩니다.
디자인 타임에 리소스를 사용하려면 리소스를 별도의 리소스 사전에 추가한 다음 이 사전을 App.xaml과 사용자 컨트롤의 XAML에 포함시킵니다. 모든 StaticResource 참조를 DynamicResource 참조로 변경합니다. 다음 코드 예제에서는 디자인 타임에 리소스를 사용할 수 있도록 리소스 사전을 공유하는 방법을 보여 줍니다.
UserControl1.xaml
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Pack URI 구문
응용 프로그램에 대해 상대적인 리소스 참조를 사용하면 안 됩니다. 다음 코드 예제에서는 디자인 타임에 실패할 수 있는 응용 프로그램 기반 구문을 보여 줍니다.
<Image Name="image1" Source="pack://application:,,,/Image1.bmp" />
이러한 참조는 DLL이 아닌 응용 프로그램에 대해 상대적입니다. DLL에서 응용 프로그램에 상대적인 리소스 참조를 사용하면 DLL이 부모 응용 프로그램의 리소스에 종속됩니다. 이 방법을 사용할 경우 참조가 손상될 수 있으며 디자인 타임에 올바르게 작동하지 않을 수도 있습니다.
응용 프로그램에 상대적인 리소스 참조를 사용하는 대신 DLL 자체에 리소스를 추가하고 구성 요소 기반 리소스 참조를 사용하는 것이 좋습니다. 자세한 내용은 Windows Presentation Foundation의 Pack URI를 참조하십시오.
다음 코드 예제에서는 권장되는 방법인 구성 요소 기반 구문을 보여 줍니다.
<Image Name="image1" Source="/TestHostApp;component/Image1.bmp" />
<Image Name="image1" Source="pack://application:,,,/TestHostApp;component/Image1
프로그래밍 팁
다음은 WPF Designer를 사용할 때 도움이 되는 몇 가지 프로그래밍 팁입니다.
사용자 지정 컨트롤을 WPF Designer에서 로드하려면 정의한 모든 종속성 속성에 대해 CLR get 및 set 메서드를 제공해야 합니다. 자세한 내용은 사용자 지정 종속성 속성을 참조하십시오.
ComboBox 형식의 표시기(Adorner)는 지원되지 않습니다.
타사 Windows Forms 컨트롤을 사용하려면 해당 Controls 컬렉션에 공급업체 컨트롤 인스턴스가 있는 UserControl 형식을 만듭니다. 자세한 내용은 연습: WPF 응용 프로그램에서 타사 Windows Forms 컨트롤 호스팅을 참조하십시오.
FlowDocument에 대해서는 디자인 타임이 직접적으로 지원되지 않습니다. 포함된 FlowDocument에 WPF Designer를 사용하려면 먼저 Frame컨트롤에 FlowDocument를 배치한 다음 WPF Designer에서 이 컨트롤을 사용합니다.
최선의 프로그래밍 방법
다음은 WPF Designer용의 보다 강력한 코드를 작성하기 위한 몇 가지 최선의 프로그래밍 방법입니다.
항상 using 문 또는 try/finally 블록 내에 편집 범위를 래핑합니다. 예외가 발생하면 Dispose 호출에서 변경이 중단됩니다. 자세한 내용은 ModelEditingScope를 참조하십시오.
ModelEditingScope를 사용하여 컨트롤을 한 컨테이너에서 다른 컨테이너로 이동합니다. 이렇게 하지 않으면 예외가 발생합니다.
기본값을 제거하려면 WPF와 WPF Designer에서 속성 값을 기본값으로 설정하지 않도록 합니다. Height와 같은 NaN 값의 경우 NaN을 할당하는 대신 ClearValue 메서드를 호출합니다.
속성에서 값을 검색할 때 속성의 계산 값을 사용합니다. 즉, ModelItem의 GetCurrentValue 메서드 대신 ComputedValue 속성을 사용합니다. GetCurrentValue 메서드는 XAML에 저장된 바인딩 및 다른 식을 반환하므로 간혹 캐스팅 예외가 발생할 수 있습니다.