WPF 設計工具載入失敗移難排解
更新:2007 年 11 月
Windows Presentation Foundation (WPF) Designer for Visual Studio 包含精細且可延伸的視覺化設計工具以供呈現 XAML。如果您的 XAML 檔案沒有載入到設計工具中,有幾種方式可以讓您試著去了解是什麼地方出了問題。本主題將說明一些秘訣和技巧,協助您針對 WPF 設計工具載入失敗進行疑難排解。
注意事項: |
---|
本主題提供的許多技巧也同樣適用於 Expression Blend。 |
疑難排解步驟
下列步驟可協助您針對 WPF 設計工具載入失敗進行疑難排解。
閱讀所收到的任何例外狀況訊息。
這似乎是顯而易見的步驟,但是如果您收到例外狀況,請詳讀訊息。在某些情況下,這可幫助您快速診斷問題。如需詳細資訊,請參閱 WPF 設計工具的錯誤偵錯和解譯。
判斷問題是否出於實作 (Implementation)。
建置並執行應用程式,判斷問題只是實作導致的,還是出於與 WPF 設計工具的互動。如果應用程式能夠建置並執行,則設計階段錯誤可能是實作造成的。
使用 Visual Studio 偵錯工具,在設計階段逐步執行程式碼。如需詳細資訊,請參閱逐步解說:在設計階段偵錯 WPF 自訂控制項。
判斷問題是否為載入錯誤。
如果 [設計] 檢視因例外狀況而無法載入,問題可能就是載入錯誤。如果您有自訂程式碼在設計階段載入,而在設計階段發生例外狀況或載入失敗,請參閱本主題的撰寫設計階段的程式碼一節。如果您使用資源而這些資源並未載入,請參閱本主題的設計階段的 UserControl 和自訂控制項資源一節。
檢視在設計階段載入的程式碼。
撰寫也能在設計階段執行的程式碼有兩種方式。第一種方式是藉由檢查類別的輸入參數,來撰寫防衛性程式碼。第二種方式則是藉由呼叫 GetIsInDesignMode 方法,檢查設計模式是否為作用中。如需詳細資訊,請參閱本主題的撰寫設計階段的程式碼一節。
檢視程式碼的其他地方
請參閱本主題的程式設計提示一節,以了解使用 WPF 設計工具的一些程式設計提示。請參閱本主題的程式設計最佳實務一節,了解如何撰寫更穩固程式碼的技巧。
如果仍有問題,您可以使用 MSDN 上的 WPF 設計工具論壇,與其他使用 WPF 設計工具的開發人員交流。若要報告可能的問題或提供建議,請使用 Visual Studio and .NET Framework 意見網站。
撰寫設計階段的程式碼
確定您的程式碼能在設計階段和執行階段執行。如果您的程式碼在設計階段執行,請不要假設 Application.Current 是您的應用程式。例如,當您使用 Expression Blend 時,Current 是 Expression Blend。在設計階段,MainWindow 不是您應用程式的主視窗。導致自訂控制項在設計階段失敗的常見作業包括下列幾項。
將 Current 轉型為您自訂的 Application 子類別。
將 MainWindow 轉型為您自訂的 Window 子類別。
在 Current 或 MainWindow 上使用 FindResource 或 FindName 方法。
參考來自於 Application.Resources 的資源。Application 的設計階段執行個體 (Instance) 與您的應用程式不同,因此沒有相同的資源。
不檢查 Application.Current 或 Application.MainWindow 是否傳回 null 值。如果 Visual Studio 沒有建立應用程式物件,則 Application.Current 可能傳回 null。
不檢查 Assembly.GetEntryAssembly 是否傳回 null 值。若為 Visual Studio,此方法會傳回 null。
撰寫設計階段的程式碼有兩種方式。第一種方式是藉由檢查類別的輸入參數,例如值轉換子,來撰寫防衛性程式碼。第二種方式則是藉由呼叫 GetIsInDesignMode 方法,檢查設計模式是否為作用中。
檢查某些實作的輸入參數之所以必要,是因為設計環境為某些輸入提供的型別與執行階段環境提供的不同。
樣式選取器和值轉換子通常需要使用上述其中一種方式,才能在設計階段正常執行。
值轉換子
您的自訂 IValueConverter 實作應檢查 Convert 方法的第一個參數是否有 null 和必要的型別。下列 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 顯示自訂樣板選取器,它在執行階段使用 Application.MainWindow 來判斷哪個資源以 DataTemplate 傳回。在設計階段,這個資源可能無法使用,因此 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 時,會建立該控制項的執行個體。載入頁面或視窗的 UserControl 和自訂控制項執行個體無法使用 App.xaml 中的資源。
若要讓資源在設計階段可供使用,請將它們放入另一個資源字典,然後將該字典包含在 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>
封裝 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 設計工具的程式設計提示。
如果您要自訂控制項載入到 WPF 設計工具中,您必須針對已定義的任何相依性屬性提供 CLR get 和 set 方法。如需詳細資訊,請參閱自訂相依性屬性。
不支援型別為 ComboBox 的裝飾項。
若要使用協力廠商 Windows Form 控制項,請建立 UserControl 型別,將廠商控制項的執行個體放入其 Controls 集合。如需詳細資訊,請參閱逐步解說:在 WPF 應用程式中裝載協力廠商 Windows Form 控制項。
不直接支援 FlowDocument 的設計階段。如果您要對內嵌 FlowDocument 使用 WPF 設計工具,請先將 FlowDocument 放入 Frame 控制項,這樣就可以在 WPF 設計工具中使用它。
程式設計最佳實務
下面是一些程式設計最佳實務,說明如何為 WPF 設計工具撰寫更穩固的程式碼。
請一律將編輯範圍放在 using 陳述式或 try/finally 區塊內。如果引發例外狀況,會在 Dispose 呼叫中止變更。如需詳細資訊,請參閱 ModelEditingScope。
使用 ModelEditingScope,將控制項從一個容器移至另一個容器。未能這麼做會引發例外狀況。
在 WPF 和 WPF 設計工具中,如果要清除某個屬性值,請不要將它設為預設值。針對 NaN 值 (例如 Height),請呼叫 ClearValue 方法來代替指派 NaN。
從屬性擷取值時,請使用屬性的計算值。這表示您應使用 ComputedValue 屬性,而非 ModelItem 的 GetCurrentValue 方法。GetCurrentValue 方法會傳回繫結和其他以 XAML 儲存的運算式,因此在某些情況下可能會發生轉型例外狀況。