共用方式為


逐步解說:建立詳細資料控制項擴充功能

本逐步解說會示範如何建立詳細控制項的副檔名Visual Studio LightSwitch。 詳細資料控制項由LightSwitch以顯示實體欄位。 這點不同於Group控制項,可用來顯示整個實體,藉由以排列方式顯示每個實體的欄位。 LightSwitchSummary控制項是一種詳細資料控制項。

若要建立詳細資料控制項擴充功能,您必須執行下列工作:

  • 建立控制項的擴充功能專案.

  • 更新控制項中定義的 LightSwitch 中繼資料檔.

  • 新增資源字串.

  • 實作詳細資料控制項.

  • 增強用摘要屬性的控制項.

  • 讓可程式化的控制項.

  • 建立自訂編輯器的 DisplayProperty.

  • 處理 IsComputed 控制項中的狀態.

  • 避免不慎被刪除的項目.

  • 在 DataGrid 的控點的鍵盤巡覽.

必要條件

  • Visual Studio Professional 2012

  • Visual Studio 2012 SDK

  • LightSwitch為可擴充性套件Microsoft Visual Studio 2012

建立控制項的擴充功能專案

第一個步驟是建立專案,並新增控制項範本。

若要建立擴充程式專案

  1. 在 [功能表列上Visual Studio,選擇 [ 檔案新的專案

  2. 新的專案對話方塊中,選取LightSwitch ] 節點,然後選取 LightSwitch 延伸程式庫 (Visual Basic)LightSwitch 延伸程式庫 (C#)

  3. 名稱 欄位中,輸入 DetailControlExtension 做為您延伸程式庫名稱。 這個名稱會出現在副檔名索引標籤上的LightSwitch應用程式設計工具] 中

  4. 選擇確定 ] 按鈕來建立包含所需的擴充功能的七個專案的方案。

選擇擴充功能類型

  1. 方案總管] 中,請選取 DetailControlExtension.Lspkg 專案。

  2. 在功能表列上,選擇 [ 專案加入新項目

  3. 加入新項目 對話方塊中,選取 控制項

  4. 名稱 欄位中,輸入 DetailControl 做為您的擴充名稱。 這個名稱會出現在LightSwitch螢幕設計工具。

  5. 選擇確定 ] 按鈕。 檔案會加入至方案中的數個專案。

更新 [控制項] 圖示

兩個影像檔名為 DetailControl.png 已加入至方案中,一個在 ControlImages 資料夾的 DetailControlExtension.Client.Design 專案,而另一個在 ControlImages 資料夾的 DetailControlExtension.Design 專案。 檔案中的映像做為一個圖示。 您可以使用其中一個,可唯一識別您的控制項來取代預設的影像。

若要修改圖示影像

  1. 方案總管] 中,快顯功能表上的 DetailControl.png 檔案中 ControlImages 資料夾的 DetailControlExtension.Client.Design 專案,請選擇開啟

  2. 開啟方式 對話方塊中,選取 [小畫家],然後選擇 確定 按鈕。

  3. 在 [小畫家] 中變更的影像。 例如,變更色彩或新增圖形,然後將檔案儲存並返回Visual Studio。

  4. 選取 [ DetailControl.png 檔案,並在功能表列上,按一下編輯複製

  5. 選取 [ ControlImages 資料夾的 DetailControlExtension.Design 專案、,然後在功能表列上,選擇 [ 編輯貼上。 在訊息,詢問您是否要取代的檔案中,選擇 按鈕。

更新控制項中定義的 LightSwitch 中繼資料檔

定義控制項的中繼資料包含在.lsml 檔案是在 DetailControlExtension.Common 專案。 詳細資料控制項,重要的項目是SupportedContentItemKind、 DisplayName,以及DisplayProperty。

SupportedContentItemKind會告訴LightSwitch的控制項 ; 類型 針對 [詳細資料控制項,設定SupportedContentItemKind到Details。

DisplayName屬性會提供螢幕設計工具中所顯示的名稱。 它可定義為String,比方說 「 我詳細資料控制項 」 或 as String中的資源 ModuleResources 檔案使用的標記法$(DetailControl_DisplayName)。

DisplayProperty提供一個開發人員可以在設計階段設定來指定要顯示哪一個實體的欄位的屬性。

若要更新控制項的中繼資料

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Common 的專案中,開啟 DetailControl.lsml 檔案。

  2. 變更SupportedContentItemKind項目以SupportedContentItemKind="Details"。

    這會告訴LightSwitch控制項擴充功能是詳細資料控制項。

  3. 變更DisplayName項目以<DisplayName Value="$(DetailControl_DisplayName)" />。

    在此情況下控制項將顯示名稱從取得字串資源在接下來的步驟中,您將建立的資源檔中。

  4. 插入下列程式碼後的Control.Attributes區塊。

    <Control.Properties>
          <!-- 
            Define 'DisplayProperty' to allow the developer to select which entity field should be shown inside the detail control. 
          -->
          <ControlProperty Name="DisplayProperty"
                           PropertyType=":String"
                           CategoryName="Appearance"
                           EditorVisibility="PropertySheet">
            <ControlProperty.Attributes>
              <!-- Reference localized strings in ModuleResource.resx -->
              <DisplayName Value="$(DisplayProperty_DisplayName)" />
              <Description Value="$(DisplayProperty_Description)" />
            </ControlProperty.Attributes>
    
            <!-- Define the default value of this property to be an empty string. -->
            <ControlProperty.DefaultValueSource>
              <ScreenExpressionTree>
                <!-- Only a constant expression is supported here. -->
                <ConstantExpression ResultType=":String" Value=""/>
              </ScreenExpressionTree>
            </ControlProperty.DefaultValueSource>
    
          </ControlProperty>
        </Control.Properties>
    

    這段程式碼會告訴LightSwitch以顯示String屬性工作表。 DisplayName和Description屬性會重新定義為String資源檔中的資源。 DefaultValueSource元素會告訴LightSwitch以顯示空白的String預設狀況下。

  5. 刪除Control.SupportedDataTypes區塊。 群組控制項不能直接顯示資料,因為這個區塊是不必要的。

  6. 現在,DetailControl.lsml 檔案的完整程式碼應該類似下列的範例。

    <?xml version="1.0" encoding="utf-8" ?>
    <ModelFragment
      xmlns="https://schemas.microsoft.com/LightSwitch/2010/xaml/model"
      xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">
    
      <Control Name="DetailControl"
        SupportedContentItemKind="Details"
        DesignerImageResource="DetailControlExtension.DetailControl::ControlImage">
        <Control.Attributes>
          <DisplayName Value="$(DetailControl_DisplayName)" />
        </Control.Attributes>
        <Control.Properties>
          <!-- 
            Define 'DisplayProperty' to allow the developer to select which entity field should be shown inside the detail control. 
          -->
          <ControlProperty Name="DisplayProperty"
                           PropertyType=":String"
                           CategoryName="Appearance"
                           EditorVisibility="PropertySheet">
            <ControlProperty.Attributes>
              <!-- Reference localized strings in ModuleResource.resx -->
              <DisplayName Value="$(DisplayProperty_DisplayName)" />
              <Description Value="$(DisplayProperty_Description)" />
            </ControlProperty.Attributes>
    
            <!-- Define the default value of this property to be an empty string. -->
            <ControlProperty.DefaultValueSource>
              <ScreenExpressionTree>
                <!-- Only a constant expression is supported here. -->
                <ConstantExpression ResultType=":String" Value=""/>
              </ScreenExpressionTree>
            </ControlProperty.DefaultValueSource>
    
          </ControlProperty>
        </Control.Properties>
             </Control>
    </ModelFragment>
    

新增資源字串

ModuleResources.resx 檔案中 DetailControlExtension.Common 專案包含控制項所使用的資源。 加入字串資源,將顯示的文字,為螢幕設計工具中,如此一來,控制項在.lsml 檔案中所定義的三個字串的。

若要新增資源字串

  1. 方案總管] 中,展開 資源 中的節點 DetailControlExtension.Common 專案,然後再開啟 ModuleResources.resx 檔案。

  2. 新增下列值,以 ModuleResources.resx 檔案。

    名稱

    註解

    DetailControl_DisplayName

    我的詳細資料控制項

    在 [設計工具] 畫面中的控制項的顯示名稱。

    DisplayProperty_Description

    應用程式] 所示的屬性。

    螢幕設計工具中屬性的簡短描述字串。

    DisplayProperty_DisplayName

    顯示屬性

    在 [設計工具] 畫面中的屬性名稱。

實作詳細資料控制項

DetailControl.xamlDetailControl.xaml.vbDetailControl.xaml.cs 中的檔案簡報控制項 資料夾的 DetailControlExtension.Client 專案包含該控制項的實作。 這個樣板會建立預設的實作,而您將會取代成您自己的程式碼。

Hh290136.collapse_all(zh-tw,VS.110).gif定義相依性屬性

相依性屬性可讓您稍後切換到 Silverlight 用類似的實作自訂控制項使用者控制項的控制項。 這會允許控制項透過佈景主題的副檔名 restyled,也是建立控制項擴充功能的慣用的方法。 相依性屬性會用於開發人員所繫結至實體屬性在設計階段將控制項所顯示的定義。

若要定義相依性屬性

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Client 的專案中,開啟 DetailControl.xaml.vbDetailControl.xaml.cs 檔案。

  2. 將下列加入Imports陳述式 DetailControl.xaml.vb 檔案,或using陳述式 DetailControl.xaml.cs 檔案。

    Imports Microsoft.LightSwitch.Details
    Imports Microsoft.LightSwitch.Model
    Imports System.Windows.Data
    Imports System.Diagnostics
    
    using Microsoft.LightSwitch.Details;
    using Microsoft.LightSwitch.Model;
    using System.Windows.Data;
    using System.Diagnostics;
    
  3. 新增EntityProperty屬性在 DetailControl 類別,如下所示。

    '   The EntityProperty is bound to the data property that will be shown in the TextBox.  
    ' This allows you to use DataBinding in the control UI.        
            Public Property EntityProperty As IProperty
                Get
                    Return MyBase.GetValue(DetailControl.EntityPropertyProperty)
                End Get
                Set(value As IProperty)
                    MyBase.SetValue(DetailControl.EntityPropertyProperty, value)
                End Set
            End Property
    
            Public Shared ReadOnly EntityPropertyProperty As DependencyProperty =
                DependencyProperty.Register("EntityProperty", GetType(IProperty), GetType(DetailControl), New PropertyMetadata(Nothing))
    
    /// <summary>
            ///   The EntityProperty is bound to the data property that will be shown in the TextBox.  
            ///   This allows you to use DataBinding in the control UI.
            /// </summary>
            public IProperty EntityProperty
            {
                get { return (IProperty)GetValue(EntityPropertyProperty); }
                set { SetValue(EntityPropertyProperty, value); }
            }
    
            public static readonly DependencyProperty EntityPropertyProperty =
                DependencyProperty.Register("EntityProperty", typeof(IProperty), typeof(DetailControl), new PropertyMetadata(null));
    

Hh290136.collapse_all(zh-tw,VS.110).gif建立使用者介面

接下來,建立使用者介面中控制項 DetailControl.xaml 檔案。 程式碼會定義DetailRoot項目並設定DataContext的DetailRoot。 它也會定義TextBox控制項而DetailTextBox ,並設定其Text和ReadOnly繫結,以EntityProperty您稍早建立的相依性屬性。

若要建立控制項的 UI

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Client 的專案中,開啟 DetailControl.xaml 檔案。

  2. 下列程式碼取代現有的 XAML 程式碼。

    <UserControl x:Class="DetailControlExtension.Presentation.Controls.DetailControl"
                 xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
                 x:Name="DetailRoot"
                 HorizontalAlignment="Stretch"
                 >
        <!-- 
            The DataContext of the control is a ContentItem.  However, you defined several DependencyProperties inside your control to expose necessary data to be shown on the UI.
            Here, set the DataContext of the internal implementation to your control itself, so you can use short DataBindings inside.
        -->
        <Grid DataContext="{Binding ElementName=DetailRoot}" HorizontalAlignment="Stretch">
            <!-- 
            That is the TextBox you show the property value.  The EntityProperty is a dependency property that will bind to the property to be shown on the UI.
            Bind its Value and IsReadOnly properties to the text box.
            -->
            <TextBox Name="DetailTextBox"
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch"
                     Text="{Binding EntityProperty.Value, Mode=TwoWay}" 
                     IsReadOnly="{Binding EntityProperty.IsReadOnly}" />
        </Grid>
    </UserControl>
    

Hh290136.collapse_all(zh-tw,VS.110).gif資料繫結的 EntityProperty 屬性

接下來,您將加入程式碼,以取得的值為EntityProperty屬性。 值的計算是採用目前的實體和DisplayProperty控制項屬性。 載入目前的實體LightSwitch執行階段應用程式,以及DisplayProperty值由應用程式開發人員在螢幕設計工具或程式碼中設定。 若要取得這兩個值,並正確地監視的變更,您必須加入另外兩個相依性屬性, DisplayPropertyName和ContentItem、 到DetailControl類別。

若要將屬性繫結

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Client 的專案中,開啟 DetailControl.xaml.vbDetailControl.xaml.cs 檔案。

  2. 加入下列程式碼之後的相依性屬性EntityProperty的程式碼區塊:

    ''' <summary>
            ''' DisplayPropertyName is the name of the property to be shown in the control.  When it is empty, you will use the summary property of the entity.
            ''' </summary>
            Public Property DisplayPropertyName As String
                Get
                    Return MyBase.GetValue(DetailControl.DisplayPropertyNameProperty)
                End Get
                Set(value As String)
                    MyBase.SetValue(DetailControl.DisplayPropertyNameProperty, value)
                End Set
            End Property
    
            Public Shared ReadOnly DisplayPropertyNameProperty As DependencyProperty =
                DependencyProperty.Register("DisplayPropertyName", GetType(String), GetType(DetailControl), New PropertyMetadata(AddressOf DetailControl.OnDisplayPropertyNameChanged))
    
            Private Shared Sub OnDisplayPropertyNameChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
                ' When the DisplayPropertyName is changed, reset the internal data binding.
                CType(d, DetailControl).SetContentDataBinding()
            End Sub
    
            ''' <summary>
            ''' ContentItem property is bound to the IContentItem inside the LightSwitch screen layout tree. This is created to monitor if the DataContext of the control is changed.
            ''' </summary>
            Public Property ContentItem As IContentItem
                Get
                    Return MyBase.GetValue(DetailControl.ContentItemProperty)
                End Get
                Set(value As IContentItem)
                    MyBase.SetValue(DetailControl.ContentItemProperty, value)
                End Set
            End Property
    
            Public Shared ReadOnly ContentItemProperty As DependencyProperty =
                DependencyProperty.Register("ContentItem", GetType(IContentItem), GetType(DetailControl), New PropertyMetadata(AddressOf DetailControl.OnContentItemChanged))
    
            Private Shared Sub OnContentItemChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
                ' When the ContentItem is changed, reset the internal data binding.
                CType(d, DetailControl).SetContentDataBinding()
            End Sub
    
    /// <summary>
            /// DisplayPropertyName is the name of the property to be shown in the control.  When it is empty, you will use the summary property of the entity.
            /// </summary>
            public string DisplayPropertyName
            {
                get { return (string)GetValue(DisplayPropertyNameProperty); }
                set { SetValue(DisplayPropertyNameProperty, value); }
            }
    
            public static readonly DependencyProperty DisplayPropertyNameProperty =
                DependencyProperty.Register("DisplayPropertyName", typeof(string), typeof(DetailControl), new PropertyMetadata(OnDisplayPropertyNameChanged));
    
            private static void OnDisplayPropertyNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                // When the DisplayPropertyName is changed, reset the internal data binding.
                ((DetailControl)d).SetContentDataBinding();
            }
    
            /// <summary>
            /// ContentItem property is bound to the IContentItem inside the LightSwitch screen layout tree. This is created to monitor if the DataContext of the control is changed.
            /// </summary>
            public IContentItem ContentItem
            {
                get { return (IContentItem)GetValue(ContentItemProperty); }
                set { SetValue(ContentItemProperty, value); }
            }
    
            public static readonly DependencyProperty ContentItemProperty = DependencyProperty.Register("ContentItem",
                        typeof(IContentItem), typeof(DetailControl), new PropertyMetadata(OnContentItemChanged));
    
            private static void OnContentItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                // When the ContentItem is changed, reset the internal data binding.
                ((DetailControl)d).SetContentDataBinding();
            }
    

    您也必須設定的繫結 DisplayPropertyNameContentItem 時 ControlName 會建立。

  3. 加入下列程式碼,貼入DetailControl建構函式之後InitializeComponent呼叫。

    ' Create two data bindings to get the value of the current ContentItem, and the DisplayProperty chosen by the user.
                '  The DisplayProperty can be updated in the development phase when the developer is using the run-time screen designer, or can be changed by screen APIs.
                '  It is necessary to handle the property change event in the extension control.  A data binding can help you monitor the change easily.
                MyBase.SetBinding(DetailControl.ContentItemProperty, New Binding())
                MyBase.SetBinding(DetailControl.DisplayPropertyNameProperty, New Binding("Properties[DetailControlExtension:DetailControl/DisplayProperty]"))
    
    // Create two data bindings to get the value of the current ContentItem, and the DisplayProperty chosen by the user.
                //  The DisplayProperty can be updated in the development phase when the developer is using the run-time screen designer, or can be changed by screen APIs.
                //  It is necessary to handle the property changed event in the extension control.  A data binding can help us to monitor the change easily.
                this.SetBinding(ContentItemProperty, new Binding());
                this.SetBinding(DisplayPropertyNameProperty, new Binding("Properties[DetailControlExtension:DetailControl/DisplayProperty]"));
    

    您也必須加入 SetContentDataBinding 函式類別。

  4. 加入下列程式碼,以DetailControl類別後OnContentItemChanged區塊。

    Private Sub SetContentDataBinding()
                Me.ClearValue(EntityPropertyProperty)
    
                If ContentItem IsNot Nothing Then
                    ' A detail control can only be bound to an entity object.  You can get the type from the ContentItem.
                    Dim entityType As IEntityType = TryCast(ContentItem.ResultingDataType, IEntityType)
                    Debug.Assert(entityType IsNot Nothing, "Detail Control can only bind to an entity type.")
    
                    If entityType IsNot Nothing Then
                        Dim displayPropertyName As String = Me.DisplayPropertyName
    
    
                        Dim entityProperty As IEntityPropertyDefinition = entityType.Properties.FirstOrDefault(Function(p) [String].Equals(p.Name, displayPropertyName, StringComparison.OrdinalIgnoreCase))
                        If String.IsNullOrEmpty(displayPropertyName) Then
                            entityProperty = entityType.Properties.FirstOrDefault(Function(p) String.Equals(p.Name, displayPropertyName, StringComparison.OrdinalIgnoreCase))
                        End If
    
                        If entityProperty IsNot Nothing Then
                            ' Set data binding to the entity property.
                            ' Because the DataContext of the current control is always a content item, the binding path starts from a ContentItem.  ContentItem.Value should be the entity object.
                            '  Entity.Details.Properties.[PropertyName] allows you to get the object representing a data property.  A short version, "Value.[PropertyName]", can be used to access the
                            '  value directly.  But using the data property object, you can access additional status, like whether it is readonly, and whether it has a validation error.
    
                            Me.SetBinding(EntityPropertyProperty, New Binding([String].Format(System.Globalization.CultureInfo.InvariantCulture, "Value.Details.Properties.{0}", entityProperty.Name)))
                        End If
                    End If
                End If
            End Sub
    
    private void SetContentDataBinding()
            {
                this.ClearValue(EntityPropertyProperty);
    
                if (ContentItem != null)
                {
                    // A detail control can only be bound to an entity object.  We can get the type from the ContentItem.
                    IEntityType entityType = ContentItem.ResultingDataType as IEntityType;
                    Debug.Assert(entityType != null, "Detail Control can only bind to an entity type.");
    
                    if (entityType != null)
                    {
                        string displayPropertyName = this.DisplayPropertyName;
    
                        IEntityPropertyDefinition entityProperty =
    
                                entityType.Properties.FirstOrDefault(p => String.Equals(p.Name, displayPropertyName, StringComparison.OrdinalIgnoreCase));
                         if (string.IsNullOrEmpty(displayPropertyName)) {
    entityProperty = entityType.Properties.FirstOrDefault(p => string.Equals(p.Name, displayPropertyName, StringComparison.OrdinalIgnoreCase));
    }
    
                        if (entityProperty != null)
                        {
                            // Set data binding to the entity property.
                            // Because the DataContext of the current control is always a content item, the binding path starts from a ContentItem.  ContentItem.Value should be the entity object.
                            //  Entity.Details.Properties.[PropertyName] allows us to get the object representing a data property.  A short version "Value.[PropertyName]" can be used to access the
                            //  value directly.  But using the data property object, we can access additional status, like whether it is readonly, and whether it has a validation error.
                            this.SetBinding(EntityPropertyProperty, new Binding(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Value.Details.Properties.{0}", entityProperty.Name)));
    
                        }
                    }
                }
            }
    

    詳細資料控制項的實作,現在已經完成,以及測試它在LightSwitch。

Hh290136.collapse_all(zh-tw,VS.110).gif測試詳細資料控制項

您可以測試詳細資料控制項,在實驗性的執行個體的Visual Studio。 如果您尚未已經測試另LightSwitch擴充性專案中,您必須先啟用實驗性的執行個體。

若要啟用實驗性的執行個體

  1. 方案總管] 中,請選取 DetailControlExtension.Vsix 專案。

  2. 在功能表列上,選擇 [ 專案DetailControlExtension.Vsix 屬性

  3. 偵錯 索引標籤上,在 起始動作,選擇 [ 起始外部程式

  4. 輸入的路徑Visual Studio可執行檔上 devenv.exe。

    根據預設,在 32 位元系統上,路徑檔必要 Visual Studio 11.0\Common7\IDE\devenv.exe。 在 64 位元系統上,則檔的檔案 (x86) \Microsoft Visual Studio 的 11.0\Common7\IDE\devenv.exe。

  5. 命令列引數 欄位中,輸入 /rootsuffix Exp 命令列引數。

    注意事項注意事項

    所有後續LightSwitch擴充性專案也會使用這項設定,根據預設值。

若要測試詳細資料控制項

  1. 在功能表列上,選擇 [ 偵錯啟動偵錯。 Visual Studio 的實驗執行個體隨即開啟。

  2. 在實驗性的執行個體,在功能表列上,選擇檔案新的專案

  3. 新的專案對話方塊中,選取LightSwitch ] 節點,然後選取 LightSwitch 應用程式 (Visual Basic)LightSwitch 應用程式 (C#)

  4. 名稱 欄位中,輸入 DetailControlTest,然後選擇 確定 按鈕來建立測試專案。

  5. 在功能表列上,選擇 [ 專案DetailControlTest 屬性

  6. 在 [專案設計工具中,在副檔名 索引標籤上,選取 DetailControlExtension 核取方塊。

  7. 建立基本LightSwitch應用程式中,如果資料表已經含有數個String欄位,或連線到外部資料來源。

  8. 新增清單和詳細資料 畫面,並在螢幕設計工具中,變更 摘要 下方第一個控制項 的資料列的版面配置 項目以 我詳細控制項

  9. 屬性視窗中,選取Display Property屬性,然後輸入資料表中的 [欄位名稱。

  10. 在功能表列上,選擇 [ 偵錯啟動偵錯。 觀察的行為我詳細控制項控制應用程式中,並確認已正確地顯示欄位的資料。

增強用摘要屬性的控制項

目前控制項的實作需要有應用程式開發人員DisplayProperty正確。 否則,執行任何動作隨即出現。 若要讓控制項更容易使用,您可以移除這項需求。 如果應用程式開發人員不會提供屬性,您應該提供Summary實體的屬性會自動輸入。

若要執行這項操作,建立GetSummaryMethod函式,加入數種公用程式方法,並加入程式碼來計算預設值Summary屬性。

若要建立的 GetSummaryMethod 函式

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Client 的專案中,開啟 DetailControl.xaml.vbDetailControl.xaml.cs 檔案。

  2. 將下列程式碼加入至 DetailControl 類別。

     ''' <summary>
            ''' Calculate the summary property of an entity type.
            ''' </summary>
            ''' <param name="entityType">The entity type</param>
            ''' <returns>The summary property of the entity type</returns>
            Private Shared Function GetSummaryProperty(entityType As IEntityType) As IEntityPropertyDefinition
    
                ' We should always use the summary property assigned by the user first.
                Dim attribute As ISummaryPropertyAttribute = entityType.Attributes.OfType(Of ISummaryPropertyAttribute)().FirstOrDefault()
    
                If attribute IsNot Nothing AndAlso attribute.Property IsNot Nothing Then
                    Return attribute.Property
                End If
    
                Return DetailControl.GetDefaultSummaryProperty(entityType)
    
            End Function
    
            ''' <summary>
            ''' Calculate the default summary property of an entity type.
            ''' </summary>
            ''' <param name="entityType">The entity type</param>
            ''' <returns>The default summary property of the entity type</returns>
            Private Shared Function GetDefaultSummaryProperty(entityType As IEntityType) As IEntityPropertyDefinition
    
                ' Only simple type properties can be used.  You filtered out navigation properties...
                Dim simpleTypeProperties As IEnumerable(Of IEntityPropertyDefinition) = entityType.Properties.Where(Function(p) TypeOf p.PropertyType Is ISimpleType)
    
                ' If there is any string property, you pick up the first one as the default summary property.  Otherwise, you get the first property that can be represented as a string.
                '  ModelUtilities is a utility class you add to the Common project, so it can be shared between several projects.  A project-to-project reference needs be added to make that work.
                Dim defaultSummaryProperty As IEntityPropertyDefinition =
                    simpleTypeProperties.FirstOrDefault(Function(p) ModelUtilities.GetUnderlyingSystemType(p.PropertyType) Is GetType(String))
    
                If defaultSummaryProperty Is Nothing Then
                    simpleTypeProperties.FirstOrDefault(Function(p) ModelUtilities.IsTextProperty(p))
                End If
    
                Return defaultSummaryProperty
    
            End Function
    
    /// <summary>
            /// Calculate the summary property of an entity type.
            /// </summary>
            /// <param name="entityType">The entity type</param>
            /// <returns>The summary property of the entity type</returns>
            private static IEntityPropertyDefinition GetSummaryProperty(IEntityType entityType)
            {
                // Always use the summary property assigned by the user first.
                ISummaryPropertyAttribute attribute =
                    entityType.Attributes.OfType<ISummaryPropertyAttribute>().FirstOrDefault();
    
                if (attribute != null && attribute.Property != null)
                {
                    return attribute.Property;
                }
    
                // If the summary property is not defined, calculate the default one.
                return GetDefaultSummaryProperty(entityType);
            }
    
            /// <summary>
            /// Calculate the default summary property of an entity type.
            /// </summary>
            /// <param name="entityType">The entity type</param>
            /// <returns>The default summary property of the entity type</returns>
            private static IEntityPropertyDefinition GetDefaultSummaryProperty(IEntityType entityType)
            {
                // Only simple type properties can be used.  You filtered out navigation properties...
                IEnumerable<IEntityPropertyDefinition> simpleTypeProperties = entityType.Properties.Where(p => p.PropertyType is ISimpleType);
    
                // If there is any string property, pick up the first one as the default summary property.  Otherwise, you get the first property that can be represented as a string.
                //  ModelUtilities is a utility class you add to the Common project, so it can be shared between several projects.  A project-to-project reference needs to be added to make that work.
                IEntityPropertyDefinition defaultSummaryProperty =
                    simpleTypeProperties.FirstOrDefault(p => ModelUtilities.GetUnderlyingSystemType((ISimpleType)p.PropertyType) == typeof(string)) ??
                    simpleTypeProperties.FirstOrDefault(p => ModelUtilities.IsTextProperty(p));
    
                return defaultSummaryProperty;
            }
    

    在這段程式碼, Summary如果使用者定義該應用程式中,屬性定義從應用程式中繼資料擷取。 還是遺漏的邏輯來計算摘要預設屬性,如果它沒有明確地設定應用程式開發者。 接下來您會將此邏輯。

  3. 找出SetContentDataBinding()中的方法DetailControl類別。 取代If String.IsNullOrEmpty (Visual Basic) 或if (string.IsNullOrEmpty (C#) 封鎖下列:

    If String.IsNullOrEmpty(displayPropertyName) Then
                            ' When the DisplayProperty is not set, get the summary property.
                            entityProperty = DetailControl.GetSummaryProperty(entityType)
                        Else
                            ' Otherwise, you get the entity property.
                            entityProperty = entityType.Properties.FirstOrDefault(Function(p) String.Equals(p.Name, displayPropertyName, StringComparison.OrdinalIgnoreCase))
                        End If
    
    if (String.IsNullOrEmpty(displayPropertyName))
                        {
                            // When the DisplayProperty is not set, get the summary property.
                            entityProperty = GetSummaryProperty(entityType);
                        }
                        else
                        {
                            // Otherwise, you get the entity property.
                            entityProperty = entityType.Properties.FirstOrDefault(p => String.Equals(p.Name, displayPropertyName, StringComparison.OrdinalIgnoreCase));
                        }
    

    接下來,您將在這兩個控制項中加入一些可用的公用程式方法,也在設計工具擴充功能會在稍後加入。 若要共用的不同專案間的程式碼,新增至常見專案。

若要新增公用程式方法

  1. 方案總管] 中,請選取 DetailControlExtension.Common 專案。

  2. 在功能表列上,選擇 [ 專案加入類別

  3. 加入新項目 對話方塊中,選取 類別

  4. 名稱 欄位中,輸入 ModelUtilities 做為您的類別檔案名稱,然後選擇 確定 按鈕。

  5. 以下列取代的類別檔案的內容。

    Imports System
    Imports System.Collections.Generic
    Imports System.Diagnostics
    Imports System.Linq
    Imports Microsoft.LightSwitch.Model
    
    ''' <summary>
    ''' ModelUtilities contains a few utility functions that are used in the Client, Client.Design, and Design projects.  The target projects must reference the Common project where the utility class is located.
    ''' </summary>
    Public Module ModelUtilities
    
        ''' <summary>
        ''' GetUnderlyingSystemType is a helper function to extract which CLR type is used to represent data of a LightSwitch simple type.
        ''' </summary>
        ''' <param name="dataType">LightSwitch DataType</param>
        ''' <returns>CLR type used to reprsent the data</returns>
        Public Function GetUnderlyingSystemType(dataType As ISimpleType) As Type
    
            If dataType Is Nothing Then
                Throw New ArgumentNullException("dataType")
            End If
    
            While dataType IsNot Nothing
                If TypeOf dataType Is IPrimitiveType Then
                    ' Primitive types are foundation LightSwitch data types like: String/Int32/Decimal/Date/...
                    '  This is a set that cannot be extended by a third party.
                    Return DirectCast(dataType, IPrimitiveType).ClrType
                ElseIf TypeOf dataType Is INullableType Then
                    ' NullableType represents a Nullable version of any primitive or semantic type.
                    dataType = DirectCast(dataType, INullableType).UnderlyingType
                ElseIf TypeOf dataType Is ISemanticType Then
                    ' Semantic types are types extending primitive types and adding additional semantic information like data format or validation logic.
                    dataType = DirectCast(dataType, ISemanticType).UnderlyingType
                Else
                    ' This should never happen.  A simple type in LightSwitch is either a semantic type, a nullable type or a primitive type.
                    dataType = Nothing
                End If
            End While
    
            Debug.Assert(False, "We expect all semantic types in the LightSwitch are built on the top of a primitive type.")
            Return Nothing
    
        End Function
    
        ''' <summary>
        ''' IsTextProperty returns true if a property can be represented by a string.  
        ''' In LightSwitch, a non-binary simple type property cannot be represented by a string.
        ''' </summary>
        ''' <param name="propertyDefinition">The definition of a property in the LightSwitch metadata</param>
        ''' <returns>true if the property can be represented as a string</returns>
        Public Function IsTextProperty(propertyDefinition As IPropertyDefinition) As Boolean
    
            If propertyDefinition Is Nothing Then
                Throw New ArgumentException("propertyDefinition")
            End If
    
            Dim dataType As ISimpleType = TryCast(propertyDefinition.PropertyType, ISimpleType)
            If dataType IsNot Nothing Then
                Dim clrType As Type = GetUnderlyingSystemType(dataType)
                Return clrType IsNot Nothing AndAlso clrType IsNot GetType(Byte())
            End If
            Return False
    
        End Function
    
    End Module
    
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    
    using Microsoft.LightSwitch.Model;
    
    namespace DetailControlExtension
    {
        /// <summary>
        /// ModelUtilities contains a few utility functions that are used in Client, Client.Design, and Design project.  The target project must reference the Common project, where the utility class is added.
        /// </summary>
        public static class ModelUtilities
        {
            /// <summary>
            /// GetUnderlyingSystemType is a helper function to extract which CLR type is used to represent data of a LightSwitch simple type.
            /// </summary>
            /// <param name="dataType">LightSwitch DataType</param>
            /// <returns>CLR type used to reprsent the data</returns>
            public static Type GetUnderlyingSystemType(ISimpleType dataType)
            {
                if (dataType == null)
                {
                    throw new ArgumentNullException("dataType");
                }
    
                while (dataType != null)
                {
                    if (dataType is IPrimitiveType)
                    {
                        // Primitive types are foundation LightSwitch data types like: String/Int32/Decimal/Date/...
                        //  This is a set that cannot be extended by a third party.
                        return ((IPrimitiveType)dataType).ClrType;
                    }
                    else if (dataType is INullableType)
                    {
                        // NullableType represents a Nullable version of any primitive or semantic type.
                        dataType = ((INullableType)dataType).UnderlyingType;
                    }
                    else if (dataType is ISemanticType)
                    {
                        // Semantic types are types extending primitive types and adding additional semantic information like data format or validation logic.
                        dataType = ((ISemanticType)dataType).UnderlyingType;
                    }
                    else
                    {
                        // This should never happen.  A simple type in LightSwitch is either a semantic type, a nullable type or a primitive type.
                        dataType = null;
                    }
                }
    
                Debug.Assert(false, "We expect all semantic types in the LightSwitch are built on the top of a primitive type.");
                return null;
            }
    
            /// <summary>
            /// IsTextProperty returns true if a property can be represented by a string.  
            /// In LightSwitch, a non-binary simple type property cannot be represented by a string.
            /// </summary>
            /// <param name="propertyDefinition">The definition of a property in the LightSwitch metadata</param>
            /// <returns>true if the property can be represented as a string</returns>
            public static bool IsTextProperty(IPropertyDefinition propertyDefinition)
            {
                if (propertyDefinition == null)
                {
                    throw new ArgumentNullException("propertyDefinition");
                }
    
                ISimpleType dataType = propertyDefinition.PropertyType as ISimpleType;
                if (dataType != null)
                {
                    Type clrType = GetUnderlyingSystemType(dataType);
                    return clrType != null && clrType != typeof(Byte[]);
                }
                return false;
            }
        }
    }
    

    接下來,將參考加入常見 中的專案 用戶端專案。 這樣可以讓您使用公用程式函式中定義常見 中的專案 用戶端專案。

若要加入參考

  1. 方案總管] 中,開啟快顯功能表的 DetailControl.Client 專案、 然後再選擇 加入參考

  2. 加入參考 對話方塊中,選取 專案 索引標籤,然後新增指向 DetailControl.Common 專案。

在這個時候,您可以一次測試控制項,並行為所作的變更。 確認已顯示 [摘要] 屬性,是否沒有指定值的DisplayProperty。

本逐步解說的其餘部分會說明將選擇性的功能加入至您的控制項。

讓可程式化的控制項

所有的內建LightSwitch控制項是可程式化。 若要讓開發人員可以存取它們的應用程式程式碼中的控制項,您會想要實作IContentVisual控制項的介面。

若要實作 IContentVisual 介面

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Client 的專案中,開啟 DetailControl.xaml.vbDetailControl.xaml.cs 檔案。

  2. 更新類別定義,以實作IContentVisual。

    Partial Public Class DetailControl
            Inherits UserControl
            Implements IContentVisual
    
    public partial class DetailControl : UserControl, IContentVisual
    
  3. 加入至類別的實作。

    Public ReadOnly Property Control As Object Implements Microsoft.LightSwitch.Presentation.IContentVisual.Control
                ‘Implement the IContentVisual to allow the application developer to access the TextBox control by using IContentItemProxy.SetBinding/ControlAvailable APIs.
                Get
                    Return Me.DetailTextBox
                End Get
            End Property
    
            Public Sub Show() Implements Microsoft.LightSwitch.Presentation.IContentVisual.Show
    
            End Sub
    
    // Implement the IContentVisual to allow the application developer to access the TextBox control by using IContentItemProxy.SetBinding/ControlAvailable APIs.
            object IContentVisual.Control
            {
                get { return this.DetailTextBox; }
            }
    
            void IContentVisual.Show()
            {
            }
    

建立自訂編輯器的 DisplayProperty

到目前為止的詳細資料控制項需要應用程式開發人員能夠有效實體欄位中輸入名稱顯示屬性 ] 文字方塊, 屬性視窗。 提供下拉式清單包含所有有效的選擇就是較佳的經驗。 您可以藉由建立自訂屬性編輯器來這麼做。

為了控制項擴充功能、 兩個屬性編輯器是必要的。 LightSwitch螢幕設計工具和執行階段螢幕編輯器建立在不同平台。 使用螢幕設計工具需要以 「 Windows Presentation Foundation 」 (WPF) 編輯器。 在自訂模式下運作,需要為基礎的 Silverlight 編輯器。

Hh290136.collapse_all(zh-tw,VS.110).gif建立 WPF 屬性編輯器

以 WPF 為基礎的屬性編輯器會提供從螢幕設計工具屬性的存取。 當您藉由建立擴充程式專案時LightSwitch延伸程式庫的範本, 設計專案加入至方案。 這個專案會產生組件的完整.NET Framework,而這正是您將會建立以 WPF 為基礎的屬性編輯器。

第一個步驟是將參考加入至LightSwitchDesigner命名空間及 [至常見方案中的專案。

若要加入參考

  1. 方案總管] 中,開啟快顯功能表的 DetailControl.Design 專案、 然後再選擇 加入參考

  2. 加入參考對話方塊方塊中,將參考加入至Microsoft.LightSwitch.Design.Designer.dll。

    您可以找到的組件,在 PrivateAssembly Visual Studio 的檔案夾 IDE 資料夾。

  3. 開啟快顯功能表的 DetailControl.Design ] 節點,然後選擇 加入現有項目

  4. 選取 [ 常見 您的方案中的專案,然後選取 ModelUtilities 檔案。

  5. 展開的下拉式清單上新增 按鈕,然後再選擇 [ 加入做為連結

下一步是建立 WPF 控制項,用於編輯屬性。

若要建立 WPF 控制項

  1. 方案總管] 中,開啟快顯功能表的 DetailControl.Design 專案、 然後再選擇 新增資料夾。 新的資料夾命名為編輯器。

  2. 編輯器 資料夾中,開啟快顯功能表,然後選擇 [ 加入新項目

  3. 加入新項目 對話方塊方塊中,展開 WPF ] 節點,然後選擇 使用者控制項 (WPF)

  4. 名稱 欄位中,輸入 EntityPropertyPicker,然後按一下 [ 新增

    A EntityPropertyPicker.xaml 檔案,並 EntityPropertyPicker.xaml.vbEntityPropertyPicker.xaml.cs 檔案將加入至編輯器資料夾。

  5. 編輯器 資料夾中,開啟快顯功能表,然後選擇 [ 加入新項目

  6. 加入新項目 對話方塊方塊中,展開 代碼 節點,然後選擇 [ 類別

  7. 名稱 欄位中,輸入 EntityPropertyPickerEditor ,然後按一下 [ 新增

  8. 開啟 EntityPropertyPickerEditor 類別檔案,並使用下列程式碼取代內容:

    Imports System.ComponentModel.Composition
    Imports System.Windows
    Imports System.Windows.Markup
    
    Imports Microsoft.LightSwitch.Designers.PropertyPages
    Imports Microsoft.LightSwitch.Designers.PropertyPages.UI
    
    Namespace DetailControlExtension.Editors
    
        ''' <summary>
        ''' EntityPropertyPickerProvider is a component to allow LightSwitch designers in Visual Studio to create a property value editor.
        ''' The name of the editor is specified in a PropertyValueEditorName attribute.  When it is needed, a designer will use the EditorTemplate to create
        ''' a WPF control, which can be hosted inside a property sheet window.
        ''' The DataContext of this control will be an IBindablePropertyEntry object.  Through the DataContext, the control can update the property value.
        ''' </summary>
        <Export(GetType(IPropertyValueEditorProvider))>
        <PropertyValueEditorName("DetailControlExtension:EntityPropertyPicker")>
        <PropertyValueEditorType("System.String")>
        Public Class EntityPropertyPickerEditor
            Implements IPropertyValueEditorProvider
    
            Public Function GetEditor(entry As Microsoft.LightSwitch.Designers.PropertyPages.IPropertyEntry) As Microsoft.LightSwitch.Designers.PropertyPages.UI.IPropertyValueEditor Implements Microsoft.LightSwitch.Designers.PropertyPages.UI.IPropertyValueEditorProvider.GetEditor
                Return New Editor()
            End Function
    
            Private Class Editor
                Implements IPropertyValueEditor
    
                Public ReadOnly Property Context As Object Implements Microsoft.LightSwitch.Designers.PropertyPages.UI.IPropertyValueEditor.Context
                    Get
                        ' A design-time editor allows an additional Context object, which is exposed through IBindablePropertyEntry.EditorContext.  This allows the editor to have additional status.
                        ' However, the run-time designer does not support it.  It is not used in this sample.
                        Return Nothing
                    End Get
                End Property
    
                ' The DataTemplate is used by the screen designer to create the UI control on the property sheet.
                Public Function GetEditorTemplate(entry As Microsoft.LightSwitch.Designers.PropertyPages.IPropertyEntry) As System.Windows.DataTemplate Implements Microsoft.LightSwitch.Designers.PropertyPages.UI.IPropertyValueEditor.GetEditorTemplate
                    Return XamlReader.Parse(EntityPropertyPickerEditor.ControlTemplate)
                End Function
    
            End Class
    
    #Region "Constants"
    
            Private Const ControlTemplate As String =
                "<DataTemplate" +
                " https://schemas.microsoft.com/winfx/2006/xaml/presentation""" +
                " xmlns:x=""https://schemas.microsoft.com/winfx/2006/xaml""" +
                " xmlns:editors=""clr-namespace:DetailControlExtension.Editors;assembly=DetailControlExtension.Design"">" +
                "   <editors:EntityPropertyPicker/>" +
                "</DataTemplate>"
    
    #End Region
    
        End Class
    
    End Namespace
    
    using System.ComponentModel.Composition;
    using System.Windows;
    using System.Windows.Markup;
    
    using Microsoft.LightSwitch.Designers.PropertyPages;
    using Microsoft.LightSwitch.Designers.PropertyPages.UI;
    
    namespace DetailControlExtension.Editors
    {
        /// <summary>
        /// EntityPropertyPickerProvider is a component to allow LightSwitch designers in Visual Studio to create a property value editor.
    /// The name of the editor is specified in a PropertyValueEditorName attribute.  When it is needed, a designer will use the
        ///  EditorTemplate to create a WPF control, which can be hosted inside a property sheet window.
        /// The DataContext of this control will be an IBindablePropertyEntry object.  Through the DataContext, the control can update the property value.
        /// </summary>
        [Export(typeof(IPropertyValueEditorProvider))]
        [PropertyValueEditorName("DetailControlExtension:EntityPropertyPicker")]
        [PropertyValueEditorType("System.String")]
        public class EntityPropertyPickerProvider
            : IPropertyValueEditorProvider
        {
            public IPropertyValueEditor GetEditor(IPropertyEntry entry)
            {
                return new Editor();
            }
    
            private class Editor : IPropertyValueEditor
            {
                public object Context
                {
                    get
                    {
                        // A design-time editor allows an additional Context object, which is exposed through IBindablePropertyEntry.EditorContext.  This allows the editor to have additional status.
                        // However, the run-time designer does not support it.  It is not used in this sample.
                        return null;
                    }
                }
    
                // The DataTemplate is used by the screen designer to create the UI control on the property sheet.
                public DataTemplate GetEditorTemplate(IPropertyEntry entry)
                {
                    return XamlReader.Parse(ControlTemplate) as DataTemplate;
                }
            }
    
            #region Constants
    
            private const string ControlTemplate =
                "<DataTemplate" +
                " xmlns=\"https://schemas.microsoft.com/winfx/2006/xaml/presentation\"" +
                " xmlns:x=\"https://schemas.microsoft.com/winfx/2006/xaml\"" +
                " xmlns:editors=\"clr-namespace:DetailControlExtension.Editors;assembly=DetailControlExtension.Design\">" +
                "   <editors:EntityPropertyPicker/>" +
                "</DataTemplate>";
    
            #endregion
        }
    }
    

    這段程式碼會實作可由載入的元件LightSwitch設計工具會根據它的名稱,在PropertyValueEditorName屬性。 當LightSwitch設計工具會顯示標記為使用編輯器,具有相同名稱的屬性,就會建立 WPF 控制項與DataTemplate ,將之裝載在屬性表。 DataContext的控制項將被設定成IBindablePropertyEntry物件,讓控制項可以取得或設定屬性的值。

    下一步是變更擴充功能的.lsml 檔案中的控制項屬性的中繼資料。

  9. 常見 的專案中,開啟 DetailControl.lsml 檔案。

  10. 新增UIEditorID屬性,以ControlProperty項目,如下所示。

    <ControlProperty Name="DisplayProperty"
                           PropertyType=":String"
                           CategoryName="Appearance"
                           UIEditorId="DetailControlExtension:EntityPropertyPicker"
                           EditorVisibility="PropertySheet">
    
  11. 設計 的專案中,開啟 EntityPropertyPicker.xaml 檔案,並取代下列程式碼來實作控制項的預設程式碼。

    <UserControl x:Class="DetailControlExtension.Editors.EntityPropertyPicker"
                 xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
                 xmlns:d="https://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 >
        <!-- 
        DesignTimeFontSize and DesignTimeFontFamily are design-time public resource items.  
        Use them to ensure that you use the same font inside different controls on the property sheet.
        -->
        <Grid TextBlock.FontSize="{DynamicResource DesignTimeFontSize}"
              TextBlock.FontFamily="{DynamicResource DesignTimeFontFamily}"
    >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <!-- 
                That is the label to show the property name.  
                The DataContext of this control is an IBindablePropertyEntry object. Use its Entry property to get the IPropertyEntry.
            -->
            <TextBlock x:Name="ComboBoxLabel"
                       Text="{Binding Entry.DisplayName, Mode=OneWay}"
                       TextWrapping="WrapWithOverflow"
                       ToolTip="{Binding Entry.Description, Mode=OneWay}"
                       Margin="0,0,0,2"/>
    
            <!-- 
                This is the ComboBox to pick up the value.  The DataContext of this control is an IBindablePropertyEntry object. Use its Entry property to get the IPropertyEntry.
                Use the 'GetTextPropertiesConverter' to collect all valid choices.  The PropertyValue.Value is the value of the DisplayProperty of the control.
            -->
            <ComboBox x:Name="ComboBox" 
                      Grid.Row ="1"
                      SelectedItem="{Binding Entry.PropertyValue.Value}"
                      >
            </ComboBox>
        </Grid>
    
    </UserControl>
    

    XAML 程式碼會將Label以顯示屬性名稱,以及ComboBox以顯示目前的值。 接下來,您將加入一些程式碼,以取得清單的ComboBox。 若要執行這項操作,您必須知道確切的實體型別繫結至控制項。

    螢幕設計工具中,在所公開之屬性的內容IPropertyValue.ModelItem。 針對控制項屬性, ModelItem的執行個體永遠是IContentItemDefinition正在使用該控制項。 此處,單一屬性, IContentItemDefinition.DataType,定義哪個實體類型繫結至控制項。 對於ComboBox,就需要一份該實體型別中的所有屬性的名稱。 藉由建立轉換子,可能會產生這份清單。

  12. 關閉並儲存 EntityPropertyPicker.xaml 檔案。

接下來,定義Editors命名空間,並加入程式碼來初始化程式碼後置檔案中的編輯器。

若要加入程式碼

  1. 展開 EntityPropertyPicker.xaml 節點,開啟 EntityPropertyPicker.xaml.vbEntityPropertyPicker.xaml.cs 檔案,並以下列取代內容。

    Imports System.Windows.Controls
    
    Namespace Editors
    
        Public Class EntityPropertyPicker
            Inherits UserControl
    
            Public Sub New()
                InitializeComponent()
            End Sub
    
    
    
        End Class
    
    End Namespace
    
    using System.Windows.Controls;
    
    namespace DetailControlExtension.Editors
    {
         public partial class EntityPropertyPicker : UserControl
        {
            public EntityPropertyPicker()
            {
                InitializeComponent();
            }
        }
    }
    
  2. 關閉並儲存 EntityPropertyPicker.xaml.vbEntityPropertyPicker.xaml.cs 檔案。

接下來,加入另一個公用程式方法,以取得實體屬性的集合。

若要新增的公用程式方法

  1. 常見 的專案中,開啟 ModelUtilities 類別檔案。

  2. 將新的方法加入類別。

    ''' <summary>
        ''' Get all text presentable properties of a data type.
        ''' </summary>
        ''' <param name="dataType">A data type metadata in the LightSwitch</param>
        ''' <returns>A collection of properties matching the condition.</returns>
        Public Function GetAllTextProperties(dataType As IDataType) As IEnumerable(Of IPropertyDefinition)
    
            If dataType IsNot Nothing Then
                Return dataType.Properties _
                    .Where(Function(p) ModelUtilities.IsTextProperty(p)) _
                    .Cast(Of IPropertyDefinition)()
            End If
    
            Return Enumerable.Empty(Of IPropertyDefinition)()
    
        End Function
    
    /// <summary>
            /// Get all text presentable properties of a data type.
            /// </summary>
            /// <param name="dataType">A data type metadata in the LightSwitch</param>
            /// <returns>A collection of properties matching the condition.</returns>
            public static IEnumerable<IPropertyDefinition> GetAllTextProperties(IDataType dataType)
            {
                if (dataType != null)
                {
                    return dataType.Properties
                                .Where(property => ModelUtilities.IsTextProperty(property))
                                .Cast<IPropertyDefinition>();
                }
    
                return Enumerable.Empty<IPropertyDefinition>();
            }
    

    這個方法會擷取可以表示為字串資料型別,包括實體型別內的屬性的集合。

接下來,建立轉換子,將會使用GetAllTextProperties方法,在實體中取得所有屬性的名稱,這樣就可以在下拉式方塊中顯示出來。

若要建立 GetTextPropertiesConverter

  1. 編輯器 資料夾的 設計 專案,開啟快顯功能表,然後選擇 [ 加入新項目

  2. 加入新項目 對話方塊方塊中,展開 的程式碼 ] 節點,然後選擇 類別

  3. 名稱 欄位中,輸入 GetTextPropertiesConverter,然後按一下 [ 新增

  4. 開啟 GetTextPropertiesConverter 類別檔案,並加入下列程式碼。

    Imports System
    Imports System.Collections.Generic
    Imports System.Windows.Data
    
    Imports Microsoft.LightSwitch.Model
    
    Namespace Editors
    
        ''' <summary>
        ''' GetTextPropertiesConverter gets a collection of names of properties of the entity type bound to a ContentItem. 
        ''' The resulting collection is used inside a ComboBox drop-down. An empty string is always added to the collection as none of the properties has been chosen.
        ''' </summary>
    
    Public Class GetTextPropertiesConverter
            Implements IValueConverter
    
            Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
    
                Dim textProperties As List(Of String) = New List(Of String)()
    
                textProperties.Add(String.Empty)
    
                Dim contentItemDefinition As IContentItemDefinition = TryCast(value, IContentItemDefinition)
                If contentItemDefinition IsNot Nothing Then
                    Dim entityType As IEntityType = TryCast(contentItemDefinition.DataType, IEntityType)
                    If entityType IsNot Nothing Then
                        For Each p As IPropertyDefinition In ModelUtilities.GetAllTextProperties(entityType)
                            textProperties.Add(p.Name)
                        Next
                    End If
                End If
                Return textProperties
    
            End Function
    
            Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
                Throw New NotSupportedException()
            End Function
    
        End Class
    
    End Namespace
    
    using System;
    using System.Collections.Generic;
    using System.Windows.Data;
    
    using Microsoft.LightSwitch.Model;
    
    namespace DetailControlExtension.Editors
    {
        /// <summary>
        /// GetTextPropertiesConverter gets a collection of names of properties of the entity type bound to a ContentItem. 
        /// The resulting collection is used inside a ComboBox drop-down. An empty string is always added to the collection as none of the properties has been chosen.
        /// </summary>
    #if !SILVERLIGHT
        [ValueConversion(typeof(IContentItemDefinition), typeof(IEnumerable<string>))]
    #endif
        public class GetTextPropertiesConverter
            : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                List<string> textProperties = new List<string>();
    
                textProperties.Add(String.Empty);
    
                IContentItemDefinition contentItemDefinition = (IContentItemDefinition)value;
                if (contentItemDefinition != null)
                {
                    IEntityType entityType = contentItemDefinition.DataType as IEntityType;
                    if (entityType != null)
                    {
                        foreach (IPropertyDefinition property in ModelUtilities.GetAllTextProperties(entityType))
                        {
                            textProperties.Add(property.Name);
                        }
                    }
                }
                return textProperties;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    }
    

    這個轉換子會使用IContentItemDefinition,從存取哪一個 IPropertyValue,並傳回有效的屬性名稱加上是空的字串,表示屬性,這表示您已經選擇執行任何動作的預設值的集合。

  5. 編輯器 資料夾中,開啟 EntityPropertyPicker.xaml 檔案。

  6. 加入下列命名空間對應至控制項。

    xmlns:e="clr-namespace:DetailControlExtension.Editors"
    
  7. 新增資源時若要建立新的轉換程式。

    <UserControl.Resources>
            <e:GetTextPropertiesConverter x:Key="GetTextPropertiesConverter" />
        </UserControl.Resources>
    
    注意事項注意事項

    在這個時候您可能會注意到設計工具中的發生錯誤。當您建置時,就會解決這個錯誤用戶端專案。

  8. 更新ComboBox以新的項目Converter藉由設定ItemsSource屬性:

    <ComboBox x:Name="ComboBox" 
                      Grid.Row ="1"
                      SelectedItem="{Binding Entry.PropertyValue.Value}"
                      ItemsSource="{Binding Entry.PropertyValue.ModelItem, Mode=OneWay, Converter={StaticResource GetTextPropertiesConverter}}"
                      >
            </ComboBox>
    

接下來,建立另一個轉換程式,若要設定格式的屬性名稱。

若要建立 GetTextPropertiesConverter

  1. 編輯器 資料夾的 設計 專案,開啟快顯功能表,然後選擇 [ 加入新項目

  2. 加入新項目 對話方塊方塊中,展開 的程式碼 ] 節點,然後選擇 類別

  3. 名稱 欄位中,輸入 DisplayNameConverter,然後按一下 [ 新增

  4. 開啟 DisplayNameConverter 類別檔案,並加入下列程式碼。

    Imports System
    Imports System.Globalization
    Imports System.Windows.Data
    
    Namespace Editors
    
        ''' <summary>
        ''' DisplayNameConverter is a converter to append ':' to the property name.  The result is used inside the label to edit the property.
        ''' It is important not to append ':' directly inside the property name.
        ''' </summary>
    
    Public Class DisplayNameConverter
            Implements IValueConverter
    
            Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
    
                If value IsNot Nothing Then
                    Return String.Format(CultureInfo.CurrentCulture, "{0}:", value)
                End If
                Return String.Empty
    
            End Function
    
            Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
                Throw New NotSupportedException()
            End Function
    
        End Class
    
    End Namespace
    
    using System;
    using System.Windows.Data;
    
    namespace DetailControlExtension.Editors
    {
    
        /// <summary>
        /// DisplayNameConverter is a converter to append ':' to the property name.  The result is used inside the label to edit the property.
        /// It is important not to append ':' directly inside the property name.
        /// </summary>
    #if !SILVERLIGHT
        [ValueConversion(typeof(string), typeof(string))]
    #endif
        public class DisplayNameConverter
            : IValueConverter
        {
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value != null)
                {
                    return String.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}:", value);
                }
                return String.Empty;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    }
    

    這段程式碼以字串的格式設":"標記法,也就是一個捷徑的"Microsoft.LightSwitch:"。

  5. 編輯器 資料夾中,開啟 EntityPropertyPicker.xaml 檔案。

  6. 新增資源時若要建立新的轉換程式。

    <UserControl.Resources>
            <e:GetTextPropertiesConverter x:Key="GetTextPropertiesConverter" />
            <e:DisplayNameConverter x:Key="DisplayNameConverter" />
        </UserControl.Resources>
    
  7. 在功能表列上,選擇 [ 建置DetailControlExtension.Design

  8. 新增至轉換子區塊TextBlock項目。

    <TextBlock x:Name="ComboBoxLabel"
                       Text="{Binding Entry.DisplayName, Mode=OneWay, Converter={StaticResource DisplayNameConverter}}"
                       TextWrapping="WrapWithOverflow"
                       ToolTip="{Binding Entry.Description, Mode=OneWay}"
                       Margin="0,0,0,2"/>
    

接下來,加入另一個轉換程式,以顯示中的使用者易記的預設文字ComboBox當沒有實體欄位被選取。

若要建立 PropertyNameToDisplayNameConverter

  1. 資源 資料夾的 設計 專案,開啟快顯功能表,然後選擇 [ 加入新項目

  2. 加入新項目 對話方塊方塊中,展開 一般 ] 節點,然後選擇 資源檔案

  3. 名稱 欄位中,輸入 DesignResources,然後按一下 [ 新增

  4. DesignResources.resx 檔案、 加入名為"DefaultProperty_DisplayName",字串,並將它指派值為 <Summary>。

    由應用程式開發人員不指定任何值時,將由轉換器所使用這個資源的字串。

  5. 編輯器 資料夾的 設計 專案,開啟快顯功能表,然後選擇 [ 加入新項目

  6. 加入新項目 對話方塊方塊中,展開 代碼 節點,然後選擇 [ 類別

  7. 名稱 欄位中,輸入 PropertyNameToDisplayNameConverter,然後按一下 [ 新增

  8. PropertyNameToDisplayNameConverter 類別檔案,請新增下列程式碼。

    Imports System
    Imports System.Windows.Data
    
    Imports DetailControlExtension.My.Resources
    
    Namespace Editors
    
        ''' <summary>
        ''' PropertyNameToDisplayNameConverter is used to show a user-friendly name when no property has been chosen by the user.
        ''' </summary>
    
    Public Class PropertyNameToDisplayNameConverter
            Implements IValueConverter
    
            Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
    
                If TypeOf value Is String AndAlso String.IsNullOrEmpty(DirectCast(value, String)) Then
                    Return DesignResources.DefaultProperty_DisplayName
                End If
                Return value
    
            End Function
    
            Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
                Throw New NotSupportedException()
            End Function
    
        End Class
    
    End Namespace
    
    using System;
    using System.Windows.Data;
    
    using DetailControlExtension.Resources;
    
    namespace DetailControlExtension.Editors
    {
        /// <summary>
        /// PropertyNameToDisplayNameConverter is used to show a user-friendly name when no property has been chosen by the user.
        /// </summary>
    #if !SILVERLIGHT
        [ValueConversion(typeof(string), typeof(string))]
    #endif
        public class PropertyNameToDisplayNameConverter
            : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value is string && String.IsNullOrEmpty((string)value))
                {
                    return DesignResources.DefaultProperty_DisplayName;
                }
                return value;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    }
    

    這段程式碼會檢查以查看是否值ComboBox是 null String,而且如果是的話,它會取代它與DefaultProperty_DisplayName資源字串。

  9. 編輯器 資料夾中,開啟 EntityPropertyPicker.xaml 檔案。

  10. 新增資源時若要建立新的轉換子,,如下所示。

    <UserControl.Resources>
            <e:GetTextPropertiesConverter x:Key="GetTextPropertiesConverter" />
            <e:DisplayNameConverter x:Key="DisplayNameConverter" />
            <e:PropertyNameToDisplayNameConverter x:Key="PropertyNameToDisplayNameConverter" />    
    </UserControl.Resources>
    
  11. 新增ItemTemplate內封鎖ComboBox項目,如下所示。

    <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <!-- The 'PropertyNameToDisplayNameConverter' is used to convert the empty string to a developer friendly string. -->
                        <TextBlock Text="{Binding Converter={StaticResource PropertyNameToDisplayNameConverter}}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
    

屬性編輯器現在已經完成。 您可以測試它在實驗性的執行個體的Visual Studio ,並觀察 [屬性編輯器會顯示一份實體欄位。

Hh290136.collapse_all(zh-tw,VS.110).gif建立 Silverlight 屬性編輯器

如果您嘗試將設定DisplayProperty在執行階段螢幕設計工具中,您會注意到的屬性工作表仍然會顯示TextBox屬性。 到目前為止您只能建立 WPF 屬性編輯器。 Silverlight 以 「 即時螢幕設計工具 」。 因此,無法辨識的 WPF 為基礎的編輯器。 您必須建立 Silverlight 的對等。

DetailControlExtension.Client.Design 方案中的專案是您將在其中撰寫執行階段螢幕設計工具程式碼。 您可以共用某些程式碼加入至 DetailControlExtension.Design 專案 ; Silverlight 編輯器的實作就像 WPF 編輯器。

第一個步驟是加入DefaultProperty_DisplayName資源時,就像您已經做過 WPF 編輯器。

若要新增資源

  1. 資源 資料夾的 DetailControlExtension.Client.Design 專案,開啟快顯功能表,然後選擇 [ 加入新項目

  2. 加入新項目 對話方塊方塊中,展開 一般 ] 節點,然後選擇 資源檔案

  3. 名稱 欄位中,輸入 DesignResources,然後按一下 [ 新增

  4. DesignResources.resx 檔案、 加入名為"DefaultProperty_DisplayName",字串,並將它指派值為 <Summary>。

    由應用程式開發人員不指定任何值時,將由轉換器所使用這個資源的字串。

接下來,將其連結到三個轉換程式及更早版本,建立共用 [程式碼複製到公用程式檔案 DetailControlExtension.Client.Design 專案。

若要共用的轉換子程式碼

  1. 方案總管] 中,開啟快顯功能表的 DetailControl.Client.Design 專案、 然後再選擇 新增資料夾。 新的資料夾命名為編輯器。

  2. 開啟快顯功能表的 DetailControl.Client.Design ] 節點,然後選擇 加入現有項目

  3. 選取 [ 常見 您的方案中的專案,然後選取 ModelUtilities 檔案。

  4. 展開的下拉式清單上新增 按鈕,然後再選擇 [ 加入做為連結

  5. 選取 編輯器 ] 節點,開啟快顯功能表,然後選擇 加入現有項目

  6. 加入現有項目 對話方塊方塊中,找出 編輯器 ] 資料夾中的 DetailControl.Client.Design 專案綴恁寁 DisplayNameConverterGetTextPropertiesConverter,以及 PropertyNameToDisplayNameConverter 檔案。

  7. 展開的下拉式清單上新增 按鈕,然後再選擇 [ 加入做為連結

接下來,建立 Silverlight 版本屬性的編輯器。 該控制項的實作類似的 WPF 版本。 不同之處在於, DataContext這個控制項是IPropertyEntry。 因此,繫結路徑會稍有不同。

若要建立 Silverlight 控制項

  1. 方案總管] 中,開啟快顯功能表的 編輯器 中的節點 DetailControl.Client.Design 專案、 然後再選擇 加入新項目

  2. 加入新項目 對話方塊方塊中,展開 Silverlight ] 節點,然後選擇 Silverlight 使用者控制項

  3. 名稱 欄位中,輸入 ClientEntityPropertyPicker,然後按一下 [ 新增

    EntityPropertyPicker.xaml 檔案,並 EntityPropertyPicker.xaml.vbEntityPropertyPicker.xaml.cs 檔案將加入至編輯器資料夾。

  4. 使用下列控制項實作來取代現有的 XAML。

    <UserControl x:Class="DetailControlExtension.Editors.ClientEntityPropertyPicker"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:e="clr-namespace:DetailControlExtension.Editors;assembly=DetailControlExtension.Client.Design"
        mc:Ignorable="d"
        >
        <UserControl.Resources>
            <e:PropertyNameToDisplayNameConverter x:Key="PropertyNameToDisplayNameConverter" />
            <e:DisplayNameConverter x:Key="DisplayNameConverter" />
            <e:GetTextPropertiesConverter x:Key="GetTextPropertiesConverter" />
        </UserControl.Resources>
    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
    
            <!-- That is the label to show the property name.  Use a converter to show a ':' behind the name. -->
            <TextBlock x:Name="EditorLabel" 
                       Text="{Binding Path=DisplayName, Converter={StaticResource DisplayNameConverter}}"/>
    
            <!-- 
                This is the ComboBox to pick up the value.  The DataContext of this control is an IPropertyEntry object.
                Use the 'GetTextPropertiesConverter' to collect all valid choices.  The PropertyValue.Value is the value of the DisplayProperty of the control.
                In SilverLight, ItemsSource needs to be set before SelectedItem to ensure that the control will work correctly.
            -->
            <ComboBox Margin="0,1,0,0" Grid.Row="1"
                      ItemsSource="{Binding Path=PropertyValue.ModelItem, Converter={StaticResource GetTextPropertiesConverter}}"
                      SelectedItem="{Binding Path=PropertyValue.Value, Mode=TwoWay}"
                      AutomationProperties.LabeledBy="{Binding ElementName=EditorLabel}"
                      HorizontalAlignment="Stretch"
                      >
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <!-- The 'PropertyNameToDisplayNameConverter' is used to convert the empty string to a developer friendly string. -->
                        <TextBlock Text="{Binding Converter={StaticResource PropertyNameToDisplayNameConverter}}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
    
        </Grid>
    
    </UserControl>
    
  5. 關閉並儲存 ClientEntityPropertyPicker.xaml 檔案。

  6. 編輯器 資料夾中,開啟快顯功能表,然後選擇 [ 加入新項目

  7. 加入新項目 對話方塊方塊中,展開 的程式碼 ] 節點,然後選擇 類別

  8. 名稱 欄位中,輸入 ClientEntityPropertyPickerEditor ,然後按一下 [ 新增

  9. 將下列程式碼加入至類別。

    Imports System.ComponentModel.Composition
    Imports System.Windows
    Imports System.Windows.Markup
    
    Imports Microsoft.LightSwitch.Designers.PropertyPages
    Imports Microsoft.LightSwitch.RuntimeEdit
    
    Namespace DetailControlExtension.Editors
    
        ''' <summary>
        ''' EntityPropertyPickerProvider is a component to allow LightSwitch designers in Visual Studio to create a property value editor.
        ''' The name of the editor is specified in a PropertyValueEditorName attribute.  When it is needed, a designer will use the EditorTemplate to create
        ''' a WPF control, which can be hosted inside a property sheet window.
        ''' The DataContext of this control will be an IBindablePropertyEntry object.  Through the DataContext, the control can update the property value.
        ''' </summary>
        <Export(GetType(IPropertyValueEditorProvider))>
        <PropertyValueEditorName("DetailControlExtension:EntityPropertyPicker")>
        <PropertyValueEditorType("System.String")>
        Public Class ClientEntityPropertyPickerEditor
            Implements IPropertyValueEditorProvider
    
            Public Function GetEditor(entry As Microsoft.LightSwitch.Designers.PropertyPages.IPropertyEntry) As Microsoft.LightSwitch.RuntimeEdit.IPropertyValueEditor Implements Microsoft.LightSwitch.RuntimeEdit.IPropertyValueEditorProvider.GetEditor
                Return New Editor()
            End Function
    
            Private Class Editor
                Implements IPropertyValueEditor
    
                ' The DataTemplate is used by the screen designer to create the UI control on the property sheet.
                Public Function GetEditorTemplate(entry As Microsoft.LightSwitch.Designers.PropertyPages.IPropertyEntry) As System.Windows.DataTemplate Implements Microsoft.LightSwitch.RuntimeEdit.IPropertyValueEditor.GetEditorTemplate
                    Return XamlReader.Load(ClientEntityPropertyPickerEditor.ControlTemplate)
                End Function
    
            End Class
    
    
            Private Const ControlTemplate As String =
                "<DataTemplate" +
                " https://schemas.microsoft.com/winfx/2006/xaml/presentation""" +
                " xmlns:x=""https://schemas.microsoft.com/winfx/2006/xaml""" +
                " xmlns:editors=""clr-namespace:DetailControlExtension.Editors;assembly=DetailControlExtension.Client.Design"">" +
                "   <editors:ClientEntityPropertyPicker/>" +
                "</DataTemplate>"
    
        End Class
    End Namespace
    
    using System.ComponentModel.Composition;
    using System.Windows;
    using System.Windows.Markup;
    using Microsoft.LightSwitch.Designers.PropertyPages;
    using Microsoft.LightSwitch.RuntimeEdit;
    
    namespace DetailControlExtension.Editors
    {
        /// <summary>
        /// ClientEntityPropertyPickerEditorProvider is a component that is used by the run-time screen editor to load the property value editor.
        /// The EditorName must match the UIEditor chosen inside the .lsml metadata of the control property.
        /// </summary>
        [Export(typeof(IPropertyValueEditorProvider))]
        [PropertyValueEditorName("DetailControlExtension:EntityPropertyPicker")]
        [PropertyValueEditorType("System.String")]
        public class ClientEntityPropertyPickerEditorProvider
            : IPropertyValueEditorProvider
        {
            public IPropertyValueEditor GetEditor(IPropertyEntry entry)
            {
                return new Editor();
            }
    
            private class Editor : IPropertyValueEditor
            {
                /// <summary>
                /// The DataTemplate is used by the run-time screen editor to create the UI control on its property sheet.
                /// </summary>
                /// <param name="entry"></param>
                /// <returns></returns>
                public DataTemplate GetEditorTemplate(IPropertyEntry entry)
                {
                    return (DataTemplate)XamlReader.Load(ControlTemplate);
                }
            }
    
            #region Constants
    
            private const string ControlTemplate =
                "<DataTemplate" +
                " xmlns=\"https://schemas.microsoft.com/winfx/2006/xaml/presentation\"" +
                " xmlns:x=\"https://schemas.microsoft.com/winfx/2006/xaml\"" +
                " xmlns:editors=\"clr-namespace:DetailControlExtension.Editors;assembly=DetailControlExtension.Client.Design\">" +
                "   <editors:ClientEntityPropertyPicker/>" +
                "</DataTemplate>";
    
            #endregion
        }
    }
    

    同樣地,這段程式碼類似於您加入 WPF 專案中的程式碼。

  10. 展開 ClientEntityPropertyPicker.xaml 節點,開啟 ClientEntityPropertyPicker.xaml.vbClientEntityPropertyPicker.xaml.cs 檔案,並以下列取代內容。

    Namespace Editors
    
        ''' <summary>
        ''' The ClientEntityPropertyPicker is used by the run-time screen designer to edit the DisplayProperty value.
        ''' </summary>
        Partial Public Class ClientEntityPropertyPicker
            Inherits UserControl
    
            Public Sub New()
                InitializeComponent()
            End Sub
    
        End Class
    
    End Namespace
    
    using System.Windows.Controls;
    
    namespace DetailControlExtension.Editors
    {
        /// <summary>
        /// The ClientEntityPropertyPicker is used by the run-time screen designer to edit the DisplayProperty value.
        /// </summary>
        public partial class ClientEntityPropertyPicker : UserControl
        {
            public ClientEntityPropertyPicker()
            {
                InitializeComponent();
            }
        }
    }
    
  11. 關閉並儲存 ClientEntityPropertyPicker.xaml.vbClientEntityPropertyPicker.xaml.cs 檔案。

如此即完成屬性編輯器。 您現在可以測試控制項,並觀察在執行階段螢幕設計工具的更新的行為。

處理 IsComputed 控制項中的狀態

當DisplayProperty控制項的屬性是計算的欄位、 控制項之前的計算方式] 屬性會顯示屬性的舊值。 這不是預期的模式LightSwitch控制項。 LightSwitch控制項通常表示計算的值仍被計算,讓一般使用者不做決策,舊的值為基礎。

對於值的控制項,這通常處理直接地ContentItemPresenter裝載控制項。 詳細資料控制項中有多個繫結路徑,因為您必須撰寫一些程式碼中處理狀態。 為了支援這種情況,您首先必須加入新相依性屬性以 DetailControl.xaml.vbDetailControl.xaml.cs 檔案中用戶端專案。

若要加入相依性屬性

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Client 的專案中,開啟 DetailControl.xaml.vbDetailControl.xaml.cs 檔案。

  2. 將下列程式碼加入至 DetailControl 類別:

    ''' <summary>
            ''' The IsComputed property is bound to the IsComputed status of a computed property.  For other properties, it returns the default value, which is true.
            ''' </summary>
            Public Property IsComputed As Boolean
                Get
                    Return MyBase.GetValue(DetailControl.IsComputedProperty)
                End Get
                Set(value As Boolean)
                    MyBase.SetValue(DetailControl.IsComputedProperty, value)
                End Set
            End Property
    
            Public Shared ReadOnly IsComputedProperty As DependencyProperty =
                        DependencyProperty.Register("IsComputed", GetType(Boolean), GetType(DetailControl), New PropertyMetadata(True))
    
    /// <summary>
            /// The IsComputed property is bound to the IsComputed status of a computed property.  For other properties, it returns the default value, which is true.
            /// </summary>
            public bool IsComputed
            {
                get { return (bool)GetValue(IsComputedProperty); }
                set { SetValue(IsComputedProperty, value); }
            }
    
            public static readonly DependencyProperty IsComputedProperty =
                DependencyProperty.Register("IsComputed", typeof(bool), typeof(DetailControl), new PropertyMetadata(true));
    

    接下來,加入一些程式碼以SetContentDataBinding屬性繫結的方法。

  3. 加入下列程式碼,重設的繫結的開頭SetContentDataBinding()方法。

    MyBase.ClearValue(DetailControl.IsComputedProperty)
    
    this.ClearValue(IsComputedProperty);
    
  4. 接下來只在設定的那一行之後中加入下列程式碼的方法主體內EntityPropertyProperty。

    ' Set data binding to the IsComputed status of a computed property.  IsComputed is a property of a computed property object.
                            MyBase.SetBinding(DetailControl.IsComputedProperty, New Binding(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Value.Details.Properties.{0}.IsComputed", entityProperty.Name)))
    
    // Set data binding to the IsComputed status of a computed property.  IsComputed is a property of a computed property object.
    this.SetBinding(IsComputedProperty, new Binding(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Value.Details.Properties.{0}.IsComputed", entityProperty.Name)));
    

接下來,將轉換程式來協助建立必要的 UI。

若要加入轉換子

  1. 方案總管] 中,開啟快顯功能表的 簡報/控制項 資料夾中的 DetailControl.Client 專案、 然後再選擇 加入新項目

  2. 加入新項目 對話方塊方塊中,展開 的程式碼 ] 節點,然後選擇 類別

  3. 名稱 欄位中,輸入 IsComputingVisibilityConverter,然後按一下 [ 新增

  4. 將下列程式碼加入至 IsComputingVisibilityConverter 類別。

    Imports System
    Imports System.Windows
    Imports System.Windows.Data
    
    Namespace DetailControlExtension.Presentation.Controls
    
        ''' <summary>
        ''' IsComputingVisibilityConverter is a value converter to turn on the IsComputing symbol when a computed property is being calculated.
        ''' </summary>
        Public Class IsComputingVisibilityConverter
            Implements IValueConverter
    
            Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
                Dim isComputed = True
                If TypeOf value Is Boolean Then
                    isComputed = DirectCast(value, Boolean)
                End If
                Return If(isComputed, Visibility.Collapsed, Visibility.Visible)
            End Function
    
            Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
                Throw New NotSupportedException()
            End Function
    
        End Class
    
    End Namespace
    
    using System;
    using System.Windows;
    using System.Windows.Data;
    
    namespace DetailControlExtension.Presentation.Controls
    {
        /// <summary>
               /// IsComputingVisibilityConverter is a value converter to turn on the IsComputing sign when a computed property is being calculated.
        /// </summary>
        public class IsComputingVisibilityConverter
    
            : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                bool isComputed = true; 
                if (value is bool) 
                {
                    isComputed = (bool)value; 
                }
                return isComputed ? Visibility.Collapsed : Visibility.Visible;
    
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    }
    

接下來,定義使用者介面中的 DetailControl.xaml 檔案。

若要定義使用者介面

  1. 方案總管] 中、 開啟 DetailControl.xaml 檔案中用戶端專案。

  2. 新增命名空間對應,如下所示。

    xmlns:ctl="clr-namespace:DetailControlExtension.Presentation.Controls;assembly=DetailControlExtension.Client"
    
  3. 加入資源字典的項目,如下所示。

    <UserControl.Resources>
            <ResourceDictionary>
                <ctl:IsComputingVisibilityConverter x:Key="IsComputingVisibilityConverter" />
            </ResourceDictionary>
        </UserControl.Resources>
    
  4. 將新的項目內 ControlName.xaml 檔案,以顯示"-"結計算屬性計算出來的符號。 這應加入在Grid項目之後, TextBox項目。

    <!-- 
            This is a sample to show an indicator to indicate a computed property is still being computed.  This overlaps the TextBox to prevent the user from making a decision based on an expired value.
            -->
            <Border BorderBrush="{StaticResource TextBoxBorderBrush}" 
                    Background="{StaticResource TextBoxBackgroundBrush}" 
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch"
                    Visibility="{Binding IsComputed, Converter={StaticResource IsComputingVisibilityConverter}}">
                <TextBlock Foreground="{StaticResource TextBoxTextBrush}" Text="-" VerticalAlignment="Center" Margin="3,0" />
            </Border>
    

    控制項屬性會被計算,而將取得的值,從DataBinding ,並設定額外的圖層的可見度Visible。 該圖層將覆蓋原始TextBox ,並顯示電腦的狀態。

避免不慎被刪除的項目

當詳細資料控制項放置於清單時,清單控制項由處理 delete 鍵。 在大多數情況下,您將不想要刪除詳細資料控制項的內容。 若要防止刪除,您會希望以處理控制項KeyUp事件。

若要處理 KeyUp 事件

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Client 的專案中,開啟 DetailControl.xaml 檔案。

  2. 加入下列程式碼,以TextBox項目之後, IsReadOnly行。

    KeyUp="DetailTextBox_KeyUp"
    
  3. 開啟 DetailControl.xaml.vbDetailControl.xaml.cs 檔案中控制項 資料夾的 DetailControlExtension.Client 專案。

  4. 加入下列的事件處理常式。

    Private Sub DetailTextBox_KeyUp(sender As Object, e As KeyEventArgs) Handles DetailTextBox.KeyUp
    
                ' Handle the KeyUp message for the Delete key inside our TextBox, so it won't go to the list, and delete the current data.
                If e.Key = Key.Delete AndAlso Not Me.DetailTextBox.IsReadOnly Then
                    e.Handled = True
                End If
    
            End Sub
    
    private void DetailTextBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
            {
                // Handle the KeyUp message for the Delete key inside our TextBox, so it won't go to the list, and delete the current data.
                if (e.Key == System.Windows.Input.Key.Delete && !this.DetailTextBox.IsReadOnly)
                {
                    e.Handled = true;
                }
            }
    

在 DataGrid 的控點的鍵盤巡覽

LightSwitch啟用有不同的行為就會顯示在控制項的支援DataGrid或類似的容器控制項。 這可能需要的效能 — 可編輯的控制項很可能會比較昂貴,無法顯示,並以然後達到正確的定位停駐行為。 如果可設定焦點的控制項顯示在DataGrid,使用者可以將焦點設為雙向DataGrid儲存格,並且也基礎時,控制 tab 鍵切換格線。 這會導致每個儲存格的兩個定位停駐點。 它會造成其他問題時DataGrid開始顯示 UI 的快取資料列,因為虛擬化。 控制項的顯示模式必須因此永遠不會對可設定焦點或啟用編輯功能。

若要防止編輯,請實作您將不會收到焦點的控制項的顯示模式版本。 您不需要建立另一個版本的控制項若要這麼做。 只需要停用TabStop在TextBox您的控制項是處於顯示模式。

若要處理的鍵盤巡覽

  1. 方案總管] 中,請在 控制項 資料夾的 DetailControlExtension.Client 的專案中,開啟 DetailControl.xaml.vbDetailControl.xaml.cs 檔案。

  2. 加入新相依性屬性以DetailControl類別,如下所示。

    Public Property AllowsTabStop As Boolean
                Get
                    Return MyBase.GetValue(DetailControl.AllowsTabStopProperty)
                End Get
                Set(value As Boolean)
                    MyBase.SetValue(DetailControl.AllowsTabStopProperty, value)
                End Set
            End Property
    
            ''' <summary>
            ''' The AllowsTabStop property is set to false when the control is inside a display mode cell.
            ''' </summary>
            Public Shared ReadOnly AllowsTabStopProperty As DependencyProperty =
                DependencyProperty.Register("AllowsTabStop", GetType(Boolean), GetType(DetailControl), New PropertyMetadata(True))
    
    /// <summary>
            /// The AllowsTabStop property is set to false when the control is inside a display mode cell.
            /// </summary>
            public bool AllowsTabStop
            {
                get { return (bool)GetValue(AllowsTabStopProperty); }
                set { SetValue(AllowsTabStopProperty, value); }
            }
    
            public static readonly DependencyProperty AllowsTabStopProperty =
                DependencyProperty.Register("AllowsTabStop", typeof(bool), typeof(DetailControl), new PropertyMetadata(true));
    
  3. 開啟 DetailControl.xaml 檔案中控制項 資料夾的 DetailControlExtension.Client 專案。

  4. 新增繫結,以TextBox項目,之後只要IsReadOnly行。

    IsTabStop="{Binding AllowsTabStop}"
    
  5. 下一步是建立控制項的顯示模式範本。 範本應該加入至DetailControlFactory由控制項範本中所產生的類別 DetailControl.xaml.vbDetailControl.xaml.cs 檔案中用戶端專案。

  6. 加入新的私用欄位如下所示。

    Private DisplayModeDataTemplate As DataTemplate
    
    private DataTemplate displayModeDataTemplate;
    
  7. 加入新的常數字串,如 「 範本 」,如下所示。

    Private Const DisplayModeControlTemplate As String = "<DataTemplate" & _
                " https://schemas.microsoft.com/winfx/2006/xaml/presentation""" & _
                " xmlns:x=""https://schemas.microsoft.com/winfx/2006/xaml""" & _
                " xmlns:ctl=""clr-namespace:DetailControlExtension.Presentation.Controls;assembly=DetailControlExtension.Client"">" & _
                "<ctl:DetailControl AllowsTabStop=""False"" />" & _
                "</DataTemplate>"
    
    private const string DisplayModeControlTemplate =
                "<DataTemplate" +
                " xmlns=\"https://schemas.microsoft.com/winfx/2006/xaml/presentation\"" +
                " xmlns:x=\"https://schemas.microsoft.com/winfx/2006/xaml\"" +
                " xmlns:ctl=\"clr-namespace:DetailControlExtension.Presentation.Controls;assembly=DetailControlExtension.Client\">" +
                "<ctl:DetailControl AllowsTabStop=\"False\" />" +
                "</DataTemplate>";
    

    您可以看到將常數字串類似於一般的範本 ; 唯一的差別在於, AllowsTabStop設定為 [ False在此情況下。

  8. 取代現存的GetDisplayModeDataTemplate與下列程式碼的方法。

    Public Function GetDisplayModeDataTemplate(contentItem As IContentItem) As DataTemplate Implements IControlFactory.GetDisplayModeDataTemplate
    If Me.displayModeDataTemplate Is Nothing Then
    Me.displayModeDataTemplate = TryCast(XamlReader.Load(DetailControlFactory.DisplayModeControlTemplate), DataTemplate)
    End If
    Return Me.displayModeDataTemplate
    End Function
    
    public DataTemplate GetDisplayModeDataTemplate(IContentItem contentItem)
            {
                if (null == this.displayModeDataTemplate)
                {
                    this.displayModeDataTemplate = XamlReader.Load(DetailControlFactory.DisplayModeControlTemplate) as DataTemplate;
                }
                return this.displayModeDataTemplate;
            }
    

    接下來,實作新的ISupportTextInput介面在DetailControl類別,並確定DataGrid往前輸入字元加入控制項時它切換至編輯模式的顯示模式的儲存格。 沒有這個角色的話,可能會遺失輸入前 DataGrid 設定編輯控制項。

  9. 新增 IsupportTextInput 至 DetailControl 類別定義的介面。

    Partial Public Class DetailControl
            Inherits UserControl
            Implements IContentVisual
            Implements ISupportTextInput
    
    public partial class DetailControl : UserControl, IContentVisual, ISupportTextInput
    
  10. 新增的實作ISupportTextInput到DetailControl類別。

    ' This method allows DataGrid to forward input to the TextBox correctly.
            Private Sub ISupportTextInput_SetText(text As String) Implements ISupportTextInput.SetText
                If Not Me.DetailTextBox.IsReadOnly Then
                    Me.DetailTextBox.Text = text
                    Me.DetailTextBox.SelectionStart = text.Length
                End If
            End Sub
    
    // This method allows DataGrid to forward input to the TextBox correctly.
            void ISupportTextInput.SetText(string text)
            {
                if (!this.DetailTextBox.IsReadOnly)
                {
                    this.DetailTextBox.Text = text;
                    this.DetailTextBox.SelectionStart = text.Length;
                }
            }
    

若您要檢查此類別的實作,您會看到它會實作標準DataTemplate ,並傳回空值,在GetDisplayModeDataTemplate方法。 如果未定義的顯示模式的樣板, LightSwitch是否永遠使用 [標準] 範本。

後續步驟

如此即完成詳細資料控制項逐步解說中 ; 您現在應該會有正常運作的控制項擴充功能,您可以重複使用在任何LightSwitch專案。 這是一個例子來詳細資料控制項。 您可以建立一個很大的差異在外觀或行為的控制項。 相同的基本步驟和原則套用到任何詳細資料控制項。

如果您將控制項擴充散佈,有兩個步驟,才會。 若要確定您的擴充功能在專案設計工具,並在顯示的資訊擴充管理員是更正,請更新 VSIX 套件內容。 如需詳細資訊,請參閱 HOW TO:設定 VSIX 套件屬性。 此外,還有幾件事,請考慮是否需將擴充散佈公開。 如需詳細資訊,請參閱 HOW TO:散發 LightSwitch 擴充功能

請參閱

工作

HOW TO:建立 LightSwitch 控制項

HOW TO:散發 LightSwitch 擴充功能

HOW TO:設定 VSIX 套件屬性

概念

定義、覆寫和使用 LightSwitch 控制項屬性

Visual Studio 2012 的 LightSwitch 擴充性工具組