逐步解說:在設計階段偵錯 WPF 自訂控制項
本逐步解說將示範如何為 Windows Presentation Foundation (WPF) 自訂控制項偵錯設計階段裝飾項。 這個裝飾項是一個核取方塊,提供簡單的自動調整大小功能。
在這個逐步解說中,您會執行下列工作:
建立 WPF 自訂控制項程式庫專案。
為設計階段中繼資料建立個別組件。
實作裝飾項提供者。
在設計階段測試控制項。
設定用於設計階段偵錯的專案。
在設計階段偵錯控制項。
完成時,您就知道要如何偵錯自訂控制項的裝飾項。
注意事項 |
---|
根據您目前使用的設定或版本,您所看到的對話方塊與功能表指令可能會與 [說明] 中描述的不同。 若要變更設定,請從 [工具] 功能表中選取 [匯入和匯出設定]。 如需詳細資訊,請參閱 使用設定。 |
必要條件
您需要下列元件才能完成此逐步解說:
- Visual Studio 2010。
建立自訂控制項
第一個步驟是為自訂控制項建立專案。 這個控制項是一個具有少量設計階段程式碼的按鈕,這個程式碼使用 GetIsInDesignMode 方法來實作設計階段行為。
建立自訂控制項
在 Visual Basic 或 Visual C# 中建立名為 AutoSizeButtonLibrary 的新 WPF 自訂控制項程式庫專案。
CustomControl1 的程式碼隨即在 [程式碼編輯器] 中開啟。
在 [方案總管] 中,將程式碼檔案的名稱變更為 AutoSizeButton.cs 或 AutoSizeButton.vb。 如果顯示訊息方塊詢問您是否要重新命名專案中的所有參考,請按一下 [是]。
在 [程式碼編輯器] 中開啟 AutoSizeButton.cs 或 AutoSizeButton.vb。
以下列程式碼取代自動產生的程式碼。 這個程式碼繼承自 Button,且當按鈕出現在設計工具中時會顯示文字「設計模式作用中」。
Imports System Imports System.Collections.Generic Imports System.Text Imports System.Windows.Controls Imports System.Windows.Media Imports System.ComponentModel ' The AutoSizeButton control implements a button ' with a custom design-time experience. Public Class AutoSizeButton Inherits Button Public Sub New() ' The following code enables custom design-mode logic. ' The GetIsInDesignMode check and the following design-time ' code are optional and shown only for demonstration. If DesignerProperties.GetIsInDesignMode(Me) Then Content = "Design mode active" End If End Sub End Class
using System; using System.Collections.Generic; using System.Text; using System.Windows.Controls; using System.Windows.Media; using System.ComponentModel; namespace AutoSizeButtonLibrary { // The AutoSizeButton control implements a button // with a custom design-time experience. public class AutoSizeButton : Button { public AutoSizeButton() { // The following code enables custom design-mode logic. // The GetIsInDesignMode check and the following design-time // code are optional and shown only for demonstration. if (DesignerProperties.GetIsInDesignMode(this)) { Content = "Design mode active"; } } } }
將專案的輸出路徑設定為 "bin\"。
建置方案。
建立設計階段中繼資料組件
設計階段程式碼部署在特殊中繼資料組件中。 在這個逐步解說中,自訂裝飾項會部署在名為 AutoSizeButtonLibrary.VisualStudio.Design 的組件中。 如需詳細資訊,請參閱提供設計階段中繼資料。
若要建立設計階段中繼資料組件
在 Visual Basic 或 Visual C# 中,將名為 AutoSizeButtonLibrary.VisualStudio.Design 的新類別庫 (Class Library) 專案加入至方案。
將專案的輸出路徑設定為 ".. \AutoSizeButtonLibrary\bin\"。 這麼做會將控制項的組件和中繼資料組件保留在同一個資料夾中,讓設計工具可進行中繼資料探索。
加入下列 WPF 組件的參考。
PresentationCore
PresentationFramework
System.Xaml
WindowsBase
加入下列 WPF 設計工具組件的參考。
Microsoft.Windows.Design.Extensibility
Microsoft.Windows.Design.Interaction
加入 AutoSizeButtonLibrary 專案的參考。
在 [方案總管] 中,將 Class1 程式碼檔案的名稱變更為 Metadata.cs 或 Metadata.vb。 如果顯示訊息方塊詢問您是否要重新命名專案中的所有參考,請按一下 [是]。
以下列程式碼取代自動產生的程式碼。 這個程式碼會建立 AttributeTable,會將自訂設計階段實作附加至 AutoSizeButton 類別。
Imports System Imports System.Collections.Generic Imports System.Text Imports System.ComponentModel Imports System.Windows.Media Imports System.Windows.Controls Imports System.Windows Imports AutoSizeButtonLibrary Imports Microsoft.Windows.Design.Features Imports Microsoft.Windows.Design.Metadata Imports AutoSizeButtonLibrary.VisualStudio.Design ' The ProvideMetadata assembly-level attribute indicates to designers ' that this assembly contains a class that provides an attribute table. <Assembly: ProvideMetadata(GetType(AutoSizeButtonLibrary.VisualStudio.Design.Metadata))> ' Container for any general design-time metadata to initialize. ' Designers look for a type in the design-time assembly that ' implements IProvideAttributeTable. If found, designers instantiate ' this class and access its AttributeTable property automatically. Friend Class Metadata Implements IProvideAttributeTable ' Accessed by the designer to register any design-time metadata. Public ReadOnly Property AttributeTable() As AttributeTable _ Implements IProvideAttributeTable.AttributeTable Get Dim builder As New AttributeTableBuilder() builder.AddCustomAttributes( _ GetType(AutoSizeButton), _ New FeatureAttribute(GetType(AutoSizeAdornerProvider))) Return builder.CreateTable() End Get End Property End Class
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Windows.Media; using System.Windows.Controls; using System.Windows; using AutoSizeButtonLibrary; using Microsoft.Windows.Design.Features; using Microsoft.Windows.Design.Metadata; using AutoSizeButtonLibrary.VisualStudio.Design; // The ProvideMetadata assembly-level attribute indicates to designers // that this assembly contains a class that provides an attribute table. [assembly: ProvideMetadata(typeof(AutoSizeButtonLibrary.VisualStudio.Design.Metadata))] namespace AutoSizeButtonLibrary.VisualStudio.Design { // Container for any general design-time metadata to initialize. // Designers look for a type in the design-time assembly that // implements IProvideAttributeTable. If found, designers instantiate // this class and access its AttributeTable property automatically. internal class Metadata : IProvideAttributeTable { // Accessed by the designer to register any design-time metadata. public AttributeTable AttributeTable { get { AttributeTableBuilder builder = new AttributeTableBuilder(); builder.AddCustomAttributes( typeof(AutoSizeButton), new FeatureAttribute(typeof(AutoSizeAdornerProvider))); return builder.CreateTable(); } } } }
儲存方案。
實作裝飾項提供者。
裝飾項提供者實作在名為 AutoSizeAdornerProvider 的型別中。 這個裝飾項 FeatureProvider 可讓您在設計階段,設定控制項的 Height 和 Width 屬性。
若要實作裝飾項提供者
將名為 AutoSizeAdornerProvider 的新類別加入至 AutoSizeButtonLibrary.VisualStudio.Design 專案。
在 AutoSizeAdornerProvider 的 [程式碼編輯器] 中,以下列程式碼取代自動產生的程式碼。 這個程式碼會實作 PrimarySelectionAdornerProvider,而後者會根據 CheckBox 控制項提供裝飾項。
Imports System Imports System.Collections.Generic Imports System.Text Imports System.Windows.Input Imports System.Windows Imports System.Windows.Automation Imports System.Windows.Controls Imports System.Windows.Media Imports System.Windows.Shapes Imports Microsoft.Windows.Design.Interaction Imports Microsoft.Windows.Design.Model ' The following class implements an adorner provider for the ' AutoSizeButton control. The adorner is a CheckBox control, which ' changes the Height and Width of the AutoSizeButton to "Auto", ' which is represented by Double.NaN. Public Class AutoSizeAdornerProvider Inherits PrimarySelectionAdornerProvider Private settingProperties As Boolean Private adornedControlModel As ModelItem Private autoSizeCheckBox As CheckBox Private autoSizeAdornerPanel As AdornerPanel ' The constructor sets up the adorner control. Public Sub New() autoSizeCheckBox = New CheckBox() autoSizeCheckBox.Content = "AutoSize" autoSizeCheckBox.IsChecked = True autoSizeCheckBox.FontFamily = AdornerFonts.FontFamily autoSizeCheckBox.FontSize = AdornerFonts.FontSize autoSizeCheckBox.Background = CType( _ AdornerResources.FindResource(AdornerColors.RailFillBrushKey), _ Brush) End Sub ' The following method is called when the adorner is activated. ' It creates the adorner control, sets up the adorner panel, ' and attaches a ModelItem to the AutoSizeButton. Protected Overrides Sub Activate(ByVal item As ModelItem) ' Save the ModelItem and hook into when it changes. ' This enables updating the slider position when ' a new background value is set. adornedControlModel = item AddHandler adornedControlModel.PropertyChanged, AddressOf AdornedControlModel_PropertyChanged ' All adorners are placed in an AdornerPanel ' for sizing and layout support. Dim panel As AdornerPanel = Me.Panel ' Set up the adorner's placement. AdornerPanel.SetAdornerHorizontalAlignment(autoSizeCheckBox, AdornerHorizontalAlignment.OutsideLeft) AdornerPanel.SetAdornerVerticalAlignment(autoSizeCheckBox, AdornerVerticalAlignment.OutsideTop) ' Listen for changes to the checked state. AddHandler autoSizeCheckBox.Checked, AddressOf autoSizeCheckBox_Checked AddHandler autoSizeCheckBox.Unchecked, AddressOf autoSizeCheckBox_Unchecked ' Run the base implementation. MyBase.Activate(item) End Sub ' The Panel utility property demand-creates the ' adorner panel and adds it to the provider's ' Adorners collection. Public ReadOnly Property Panel() As AdornerPanel Get If Me.autoSizeAdornerPanel Is Nothing Then Me.autoSizeAdornerPanel = New AdornerPanel() ' Add the adorner to the adorner panel. Me.autoSizeAdornerPanel.Children.Add(autoSizeCheckBox) ' Add the panel to the Adorners collection. Adorners.Add(autoSizeAdornerPanel) End If Return Me.autoSizeAdornerPanel End Get End Property ' The following code handles the Checked event. ' It autosizes the adorned control's Height and Width. Sub autoSizeCheckBox_Checked(ByVal sender As Object, ByVal e As RoutedEventArgs) Me.SetHeightAndWidth(True) End Sub ' The following code handles the Unchecked event. ' It sets the adorned control's Height and Width to a hard-coded value. Sub autoSizeCheckBox_Unchecked(ByVal sender As Object, ByVal e As RoutedEventArgs) Me.SetHeightAndWidth(False) End Sub ' The SetHeightAndWidth utility method sets the Height and Width ' properties through the model and commits the change. Private Sub SetHeightAndWidth(ByVal [auto] As Boolean) settingProperties = True Dim batchedChange As ModelEditingScope = adornedControlModel.BeginEdit() Try Dim widthProperty As ModelProperty = adornedControlModel.Properties("Width") Dim heightProperty As ModelProperty = adornedControlModel.Properties("Height") If [auto] Then widthProperty.ClearValue() heightProperty.ClearValue() Else widthProperty.SetValue(20.0) heightProperty.SetValue(20.0) End If batchedChange.Complete() Finally batchedChange.Dispose() settingProperties = False End Try End Sub ' The following method deactivates the adorner. Protected Overrides Sub Deactivate() RemoveHandler adornedControlModel.PropertyChanged, _ AddressOf AdornedControlModel_PropertyChanged MyBase.Deactivate() End Sub ' The following method handles the PropertyChanged event. Sub AdornedControlModel_PropertyChanged( _ ByVal sender As Object, _ ByVal e As System.ComponentModel.PropertyChangedEventArgs) If settingProperties Then Return If e.PropertyName = "Height" Or e.PropertyName = "Width" Then Dim h As Double = CType(adornedControlModel.Properties("Height").ComputedValue, Double) Dim w As Double = CType(adornedControlModel.Properties("Width").ComputedValue, Double) If Double.IsNaN(h) And Double.IsNaN(w) Then autoSizeCheckBox.IsChecked = True Else autoSizeCheckBox.IsChecked = False End If End If End Sub End Class
using System; using System.Collections.Generic; using System.Text; using System.Windows.Input; using System.Windows; using System.Windows.Automation; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using Microsoft.Windows.Design.Interaction; using Microsoft.Windows.Design.Model; namespace AutoSizeButtonLibrary.VisualStudio.Design { // The following class implements an adorner provider for the // AutoSizeButton control. The adorner is a CheckBox control, which // changes the Height and Width of the AutoSizeButton to "Auto", // which is represented by double.NaN. public class AutoSizeAdornerProvider : PrimarySelectionAdornerProvider { bool settingProperties; private ModelItem adornedControlModel; CheckBox autoSizeCheckBox; AdornerPanel autoSizeAdornerPanel; // The constructor sets up the adorner control. public AutoSizeAdornerProvider() { autoSizeCheckBox = new CheckBox(); autoSizeCheckBox.Content = "AutoSize"; autoSizeCheckBox.IsChecked = true; autoSizeCheckBox.FontFamily = AdornerFonts.FontFamily; autoSizeCheckBox.FontSize = AdornerFonts.FontSize; autoSizeCheckBox.Background = AdornerResources.FindResource( AdornerColors.RailFillBrushKey) as Brush; } // The following method is called when the adorner is activated. // It creates the adorner control, sets up the adorner panel, // and attaches a ModelItem to the AutoSizeButton. protected override void Activate(ModelItem item) { // Save the ModelItem and hook into when it changes. // This enables updating the slider position when // a new background value is set. adornedControlModel = item; adornedControlModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler( AdornedControlModel_PropertyChanged); // All adorners are placed in an AdornerPanel // for sizing and layout support. AdornerPanel panel = this.Panel; // Set up the adorner's placement. AdornerPanel.SetAdornerHorizontalAlignment(autoSizeCheckBox, AdornerHorizontalAlignment.OutsideLeft); AdornerPanel.SetAdornerVerticalAlignment(autoSizeCheckBox, AdornerVerticalAlignment.OutsideTop); // Listen for changes to the checked state. autoSizeCheckBox.Checked += new RoutedEventHandler(autoSizeCheckBox_Checked); autoSizeCheckBox.Unchecked += new RoutedEventHandler(autoSizeCheckBox_Unchecked); // Run the base implementation. base.Activate(item); } // The Panel utility property demand-creates the // adorner panel and adds it to the provider's // Adorners collection. private AdornerPanel Panel { get { if (this.autoSizeAdornerPanel == null) { autoSizeAdornerPanel = new AdornerPanel(); // Add the adorner to the adorner panel. autoSizeAdornerPanel.Children.Add(autoSizeCheckBox); // Add the panel to the Adorners collection. Adorners.Add(autoSizeAdornerPanel); } return this.autoSizeAdornerPanel; } } // The following code handles the Checked event. // It autosizes the adorned control's Height and Width. void autoSizeCheckBox_Checked(object sender, RoutedEventArgs e) { this.SetHeightAndWidth(true); } // The following code handles the Unchecked event. // It sets the adorned control's Height and Width to a hard-coded value. void autoSizeCheckBox_Unchecked(object sender, RoutedEventArgs e) { this.SetHeightAndWidth(false); } // The SetHeightAndWidth utility method sets the Height and Width // properties through the model and commits the change. private void SetHeightAndWidth(bool autoSize) { settingProperties = true; try { using (ModelEditingScope batchedChange = adornedControlModel.BeginEdit()) { ModelProperty widthProperty = adornedControlModel.Properties["Width"]; ModelProperty heightProperty = adornedControlModel.Properties["Height"]; if (autoSize) { widthProperty.ClearValue(); heightProperty.ClearValue(); } else { widthProperty.SetValue(20d); heightProperty.SetValue(20d); } batchedChange.Complete(); } } finally { settingProperties = false; } } // The following method deactivates the adorner. protected override void Deactivate() { adornedControlModel.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler( AdornedControlModel_PropertyChanged); base.Deactivate(); } // The following method handles the PropertyChanged event. void AdornedControlModel_PropertyChanged( object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (settingProperties) { return; } if (e.PropertyName == "Height" || e.PropertyName == "Width") { double h = (double)adornedControlModel.Properties["Height"].ComputedValue; double w = (double)adornedControlModel.Properties["Width"].ComputedValue; autoSizeCheckBox.IsChecked = (h == double.NaN && w == double.NaN) ? true : false; } } } }
建置方案。
測試設計階段實作
您可以使用 AutoSizeButton 控制項,就像使用任何其他 WPF 控制項一樣。 WPF 設計工具會負責建立所有的設計階段物件。
若要測試設計階段實作
將名為 DemoApplication 的新 WPF 應用程式專案加入至方案。
MainWindow.xaml 隨即在 WPF 設計工具中開啟。
加入 AutoSizeButtonLibrary 專案的參考。
在 [XAML] 檢視中,以下列程式碼取代自動產生的程式碼。 這個 XAML 會加入 AutoSizeButtonLibrary 命名空間的參考,並加入 AutoSizeButton 自訂控制項。 按鈕會顯示在 [設計] 檢視中,並顯示文字「設計模式作用中」,表示正在設計模式下。 如果按鈕不顯示,您可能需要按一下設計工具頂端的資訊列以重新載入檢視。
<Window x:Class="DemoApplication.MainWindow" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:ab="clr-namespace:AutoSizeButtonLibrary;assembly=AutoSizeButtonLibrary" Title="Window1" Height="300" Width="300"> <Grid> <ab:AutoSizeButton Height="Auto" Width="Auto" /> </Grid> </Window>
在 [設計] 檢視中,按一下 AutoSizeButton 控制項以選取該項目。
CheckBox 控制項會出現在 AutoSizeButton 控制項上方。
取消核取核取方塊裝飾項。
控制項會縮小, 而核取方塊裝飾項會移動,以保持其與所裝飾控制項的相對位置。
設定專案進行設計階段偵錯
此時您已完成設計階段實作。 現在,您可以使用 Visual Studio 設定中斷點,並逐步執行設計階段程式碼。為了偵錯設計階段實作,您會將 Visual Studio 的另一個執行個體附加到目前的 Visual Studio 工作階段。
若要設定用於設計階段偵錯的專案
在 [方案總管] 中,以滑鼠右鍵按一下 [DemoApplication] 專案,然後選取 [設定為啟始專案]。
在 [方案總管] 中,以滑鼠右鍵按一下 [DemoApplication] 專案,然後選取 [屬性]。
在 [DemoApplication] 專案設計工具中,按一下 [偵錯] 索引標籤。
在 [起始動作] 區段中選取 [起始外部程式]。 您將會偵錯不同的 Visual Studio 執行個體。
按一下省略 () 按鈕,以開啟 [選取檔案] 對話方塊。
瀏覽 Visual Studio。 可執行檔的名稱為 devenv.exe,如果您將 Visual Studio 安裝在預設位置,其路徑就會是 "%programfiles%\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe"。
按一下 [開啟],選取 devenv.exe。
在設計階段偵錯自訂控制項
現在您已準備好在自訂控制項執行於設計階段時,對它進行偵錯。 當您啟動偵錯工作階段時,將會建立新的 Visual Studio 執行個體,且您將使用它來載入 AutoSizeButtonLibrary 方案。 當您在 WPF 設計工具中開啟 MainWindow.xaml 時,會建立自訂控制項的執行個體並開始執行。
若要在設計階段偵錯自訂控制項
在 [程式碼編輯器] 中開啟 AutoSizeButton 程式碼檔案,然後在建構函式中加入中斷點。
在 [程式碼編輯器] 中開啟 Metadata.cs 或 Metadata.vb,然後在 AttributeTable 屬性中加入中斷點。
在 [程式碼編輯器] 中開啟 AutoSizeAdornerProvider.cs 或 AutoSizeAdornerProvider.vb,然後在建構函式中加入中斷點。
在 AutoSizeAdornerProvider 類別的其餘方法中加入中斷點。
按 F5 以啟動偵錯工作階段。
第二個 Visual Studio 執行個體便會建立。 您能夠以兩種方式來區分偵錯執行個體和第二個執行個體:
偵錯執行個體在標題列中具有 [執行] 字詞。
偵錯執行個體停用 [偵錯] 工具列上的 [啟動] 按鈕。
您的中斷點是設定在偵錯執行個體中。
在第二個 Visual Studio 執行個體中,開啟 AutoSizeButtonLibrary 方案。
在 WPF 設計工具中開啟 MainWindow.xaml。
Visual Studio 的偵錯執行個體會取得焦點,而執行會在 AttributeTable 屬性中的中斷點停止。
按 F5 繼續偵錯。
執行會在 AutoSizeButton 建構函式中的中斷點停止。
按 F5 繼續偵錯。
Visual Studio 的第二個執行個體會取得焦點,而 WPF 設計工具會出現。
在 [設計] 檢視中,按一下 AutoSizeButton 控制項以選取該項目。
Visual Studio 的偵錯執行個體會取得焦點,而執行會在 AutoSizeAdornerProvider 建構函式中的中斷點停止。
按 F5 繼續偵錯。
執行會在 Activate 方法中的中斷點停止。
按 F5 繼續偵錯。
Visual Studio 的第二個執行個體會取得焦點,而 WPF 設計工具會出現。 核取方塊裝飾項會出現在 AutoSizeButton 的上方和左邊。
當您完成時,可以關閉 Visual Studio 的第二個執行個體或按一下偵錯執行個體中的 [停止偵錯] 按鈕,藉此停止偵錯工作階段。
後續步驟
您可以將多個自訂設計階段功能,加入至自訂控制項。
將自訂裝飾項加入至控制項的設計階段。 如需詳細資訊,請參閱逐步解說:建立設計階段裝飾項。
為自訂型別建立可以在 [屬性] 視窗中使用的編輯器。 如需詳細資訊,請參閱 HOW TO:建立值編輯器。
建立您可以在 [屬性] 視窗中使用的色彩編輯器。 如需詳細資訊,請參閱逐步解說:實作色彩編輯器。
請參閱
工作
參考
PrimarySelectionAdornerProvider
概念
WPF 和 Silverlight 設計工具載入失敗疑難排解