[UWP]為附加屬性和依賴屬性自定義代碼段(相容UWP和WPF)

来源:https://www.cnblogs.com/dino623/archive/2018/11/06/Snippet.html
-Advertisement-
Play Games

1. 前言 之前介紹過 "依賴屬性" 和 "附加屬性" 的代碼段,這兩個代碼段我用了很多年,一直都幫了我很多。不過這兩個代碼段我也多年沒修改過,Resharper老是提示我生成的代碼可以修改,它這麼有誠意,這次就只好從了它,順便簡單介紹下怎麼自定義代碼段。 2. VisualStudio自帶代碼段的 ...


##1. 前言 之前介紹過依賴屬性附加屬性的代碼段,這兩個代碼段我用了很多年,一直都幫了我很多。不過這兩個代碼段我也多年沒修改過,Resharper老是提示我生成的代碼可以修改,它這麼有誠意,這次就只好從了它,順便簡單介紹下怎麼自定義代碼段。

##2. VisualStudio自帶代碼段的問題 以依賴屬性為例,一個完整的依賴屬性應該包含以下部分:

  1. 註冊依賴屬性並生成依賴屬性標識符。依賴屬性標識符為一個public static readonly DependencyProperty欄位。依賴屬性標識符的名稱必須為“屬性名+Property”。在PropertyMetadata中指定屬性預設值。

  2. 實現屬性包裝器。為屬性提供 get 和 set 訪問器,在Getter和Setter中分別調用GetValue和SetValue。Getter和Setter中不應該有其它任何自定義代碼。

  3. 如果需要監視屬性值變更,可以在PropertyMetadata中定義一個PropertyChangedCallback方法。因為這個方法是靜態的,可以再實現一個同名的實例方法(可以參考ContentControl的OnContentChanged方法)。

更詳盡的規範可以參考《Framework Design Gidelines》

public int MyProperty
{
    get { return (int)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

如上面代碼所示,VisualStudio自帶的依賴屬性的代碼段propdp只實現了最基本的功能,PropertyChangedCallback等函數還得自己實現,而這部分也挺麻煩的。另外,ownerclass基本都是當前類的名字,沒有理由不使用當前類的名字作為預設值。

/// <summary>
/// 獲取或設置MyProperty的值
/// </summary>  
public int MyProperty
{
    get => (int)GetValue(MyPropertyProperty);
    set => SetValue(MyPropertyProperty, value);
}

/// <summary>
/// 標識 MyProperty 依賴屬性。
/// </summary>
public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register(nameof(MyProperty), typeof(int), typeof(MainPage), new PropertyMetadata(default(int), OnMyPropertyChanged));

private static void OnMyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{

    var oldValue = (int)args.OldValue;
    var newValue = (int)args.NewValue;
    if (oldValue == newValue)
        return;

    var target = obj as MainPage;
    target?.OnMyPropertyChanged(oldValue, newValue);
}

/// <summary>
/// MyProperty 屬性更改時調用此方法。
/// </summary>
/// <param name="oldValue">MyProperty 屬性的舊值。</param>
/// <param name="newValue">MyProperty 屬性的新值。</param>
protected virtual void OnMyPropertyChanged(int oldValue, int newValue)
{
}

上面是我自定義的代碼段,改進了這些地方:

  • getter和setter使用了表達式主體;
  • DependencyProperty.Register的第一個參數使用了nameof()關鍵字代替了字元串;
  • typeof(MainPage)這裡使用了代碼段函數ClassName()直接獲取當前類的名稱;
  • 依賴屬性的預設值使用了default()關鍵字,因為絕大部分情況下依賴屬性的預設值就是數據類型的預設值,修改預設值的工作交給DefaultStyle的Setter;
  • 添加了相對完成的PropertyChangedCallback函數;

##3. 如何自定義代碼段 基本上,一個代碼段就是一個XML文件, ###3.1 代碼段的結構

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Keywords>
                <Keyword>dp</Keyword>
            </Keywords>
            <SnippetTypes>
                <SnippetType>SurroundsWith</SnippetType>
            </SnippetTypes>
            <Title>Dependency Property</Title>
            <Author>dino.c</Author>
            <Description>For Dependency Property</Description>
            <HelpUrl>
            </HelpUrl>
            <Shortcut>dp</Shortcut>
        </Header>
        <Snippet>
            <References>
                <Reference>
                    <Assembly>
                    </Assembly>
                </Reference>
            </References>
            <Declarations>
                <Literal Editable="true">
                    <ID>PropertyType</ID>
                    <ToolTip>屬性類型</ToolTip>
                    <Default>int</Default>
                    <Function>
                    </Function>
                </Literal>
                ...
            </Declarations>
            <Code Language="csharp" Kind="method body">
                <![CDATA[     ...        ]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

如上所示,代碼段定義XML中主要分成以下幾個部分:

  1. Header:包括Keyword、Shortcut等信息。Author和Description等可有可無;
  2. Declarations:代碼段中的變數;
  3. Code:代碼段的代碼;

###3.2 代碼段中的變數 在我定義的依賴屬性代碼段中包含了三個變數:

<Literal Editable="true">
    <ID>PropertyType</ID>
    <ToolTip>屬性類型</ToolTip>
    <Default>int</Default>
    <Function>
    </Function>
</Literal>
<Literal Editable="true">
    <ID>MyProperty</ID>
    <ToolTip>屬性名</ToolTip>
    <Default>MyProperty</Default>
    <Function>
    </Function>
</Literal>
<Literal Editable="false">
    <ID>classname</ID>
    <ToolTip>類名</ToolTip>
    <Function>ClassName()</Function>
    <Default>ClassNamePlaceholder</Default>
</Literal>

其中classname不可編輯,它使用了ClassName()這個代碼段函數,返回包含已插入代碼段的類的名稱。其它可用的代碼段函數可以參考這個頁面:代碼段函數

引用變數的語法是變數名,如下所示:

public static readonly DependencyProperty $MyProperty$Property =
    DependencyProperty.Register(nameof($MyProperty$), typeof($PropertyType$), typeof($classname$), new PropertyMetadata(default($PropertyType$), On$MyProperty$Changed));

###3.3 導入代碼段 在菜單上選擇“工具->代碼片段管理器”:

在“代碼片段管理器”視窗中點擊“導入”,選中需要導入的文件後打開“導入代碼片段”,選擇位置後點擊“完成”即可完成代碼段導入:

###3.4 最終成果 依賴屬性的代碼段:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Keywords>
                <Keyword>dp</Keyword>
            </Keywords>
            <SnippetTypes>
                <SnippetType>SurroundsWith</SnippetType>
            </SnippetTypes>
            <Title>Dependency Property</Title>
            <Author>dino.c</Author>
            <Description>For Dependency Property</Description>
            <HelpUrl>
            </HelpUrl>
            <Shortcut>dp</Shortcut>
        </Header>
        <Snippet>
            <References>
                <Reference>
                    <Assembly>
                    </Assembly>
                </Reference>
            </References>
            <Declarations>
                <Literal Editable="true">
                    <ID>PropertyType</ID>
                    <ToolTip>屬性類型</ToolTip>
                    <Default>int</Default>
                    <Function>
                    </Function>
                </Literal>
                <Literal Editable="true">
                    <ID>MyProperty</ID>
                    <ToolTip>屬性名</ToolTip>
                    <Default>MyProperty</Default>
                    <Function>
                    </Function>
                </Literal>
                <Literal Editable="false">
                    <ID>classname</ID>
                    <ToolTip>類名</ToolTip>
                    <Function>ClassName()</Function>
                    <Default>ClassNamePlaceholder</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp" Kind="method body">
                <![CDATA[        
        /// <summary>
        /// 獲取或設置$MyProperty$的值
        /// </summary>  
        public $PropertyType$ $MyProperty$
        {
            get => ($PropertyType$)GetValue($MyProperty$Property);
            set => SetValue($MyProperty$Property, value);
        }

        /// <summary>
        /// 標識 $MyProperty$ 依賴屬性。
        /// </summary>
        public static readonly DependencyProperty $MyProperty$Property =
            DependencyProperty.Register(nameof($MyProperty$), typeof($PropertyType$), typeof($classname$), new PropertyMetadata(default($PropertyType$), On$MyProperty$Changed));

        private static void On$MyProperty$Changed(DependencyObject obj,DependencyPropertyChangedEventArgs args)
        {
            var oldValue = ($PropertyType$)args.OldValue;
            var newValue = ($PropertyType$)args.NewValue;
            if (oldValue == newValue)
              return;
            
            var target= obj as $classname$;
            target?.On$MyProperty$Changed(oldValue, newValue);
        }

        /// <summary>
        /// $MyProperty$ 屬性更改時調用此方法。
        /// </summary>
        /// <param name="oldValue">$MyProperty$ 屬性的舊值。</param>
        /// <param name="newValue">$MyProperty$ 屬性的新值。</param>
        protected virtual void On$MyProperty$Changed($PropertyType$ oldValue,$PropertyType$ newValue)
        {
        }]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

附加屬性的代碼段:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Keywords>
                <Keyword>ap</Keyword>
            </Keywords>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
            <Title>Attached Property</Title>
            <Author>dino.c</Author>
            <Description>For Attached Property</Description>
            <HelpUrl>
            </HelpUrl>
            <Shortcut>ap</Shortcut>
        </Header>
        <Snippet>
            <References>
                <Reference>
                    <Assembly>
                    </Assembly>
                </Reference>
            </References>
            <Declarations>
                <Literal Editable="true">
                    <ID>PropertyType</ID>
                    <ToolTip>屬性類型</ToolTip>
                    <Default>int</Default>
                    <Function>
                    </Function>
                </Literal>
                <Literal Editable="true">
                    <ID>MyProperty</ID>
                    <ToolTip>屬性名</ToolTip>
                    <Default>MyProperty</Default>
                    <Function>
                    </Function>
                </Literal>
                <Literal Editable="false">
                    <ID>classname</ID>
                    <ToolTip>類名</ToolTip>
                    <Function>ClassName()</Function>
                    <Default>ClassNamePlaceholder</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[
        /// <summary>
        /// 從指定元素獲取 $MyProperty$ 依賴項屬性的值。
        /// </summary>
        /// <param name="obj">從中讀取屬性值的元素。</param>
        /// <returns>從屬性存儲獲取的屬性值。</returns>
        public static $PropertyType$ Get$MyProperty$(DependencyObject obj) => ($PropertyType$)obj.GetValue($MyProperty$Property);

        /// <summary>
        /// 將 $MyProperty$ 依賴項屬性的值設置為指定元素。
        /// </summary>
        /// <param name="obj">對其設置屬性值的元素。</param>
        /// <param name="value">要設置的值。</param>
        public static void Set$MyProperty$(DependencyObject obj, $PropertyType$ value) => obj.SetValue($MyProperty$Property, value);

        /// <summary>
        /// 標識 $MyProperty$ 依賴項屬性。
        /// </summary>
        public static readonly DependencyProperty $MyProperty$Property =
            DependencyProperty.RegisterAttached("$MyProperty$", typeof($PropertyType$), typeof($classname$), new PropertyMetadata(default($PropertyType$), On$MyProperty$Changed));


        private static void On$MyProperty$Changed(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var oldValue = ($PropertyType$)args.OldValue;
            var newValue = ($PropertyType$)args.NewValue;
            if (oldValue == newValue)
              return;
              
            var target = obj as $classname$;
        }

        ]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

##4. 結語 雖然這兩個代碼段比較複雜,並不是每次創建依賴屬性都需要這麼完整,但刪除代碼總比增加代碼簡單得多,所以我多年來每次創建依賴屬性和附加屬性都是使用這兩個代碼段。

WPF的依賴屬性可以十分複雜,但平時用不到這麼多功能,所以和UWP使用相同的代碼段就夠了。

完整的代碼段已上傳到 Github

##5. 參考 代碼段


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述 通過合併、拆分的功能,將不同的文檔中的幻燈片進行組合形成新的的文檔,同時也可以將一個多頁的PPT文檔按頁拆分成多個不同的文檔。此功能也豐富了編程人員對PPT幻燈片的操作的選擇。下麵將分別從以下幾個要點來分別闡述通過C#來合併、拆分PPT幻燈片的方法。 示例要點 1. 合併PPT幻燈片(2種方式 ...
  • 原因:Load窗體時,窗體未顯示 解決:1、Focus()之前添加this.Show(); 2、在Shown事件中添加Focus() ...
  • 最近一直在搗鼓.NET Core方面的東西,順便寫下點東西記錄下 1、打開vs2017,新建一個項目 2、vs會自動生成一個項目,然後打開NuGet搜索MySql.Data.EntityFrameworkCore下載 3、然後在Models下麵新建一個Student類,然後再新建一個類繼承DbCon ...
  • 基於工業4.0大背景下的工業物聯網是近幾年內熱門的話題,依靠信息化技術企業可以實現數字化轉型,生產可以實現智能化製造,設備可以實現自動化運作。然而,海量的數據採集是整個建設過程的基礎環節,如何處理與利用這海量的數據是信息化技術中最重要的開發工作。那麼,基於Azure國內雲端的Iot-Hub服務是提供... ...
  • 好久沒有寫Blog,都是因為不小心墜入了愛河,時間都給我家那位了,都沒時間加班了(嗨呀,不小心撒了一下狗糧),不過,還是希望單身的趕緊找到心儀的另一半,實在找不到,那就加班啊(開個玩笑,別認真)。 二維碼神器 現在出門在外,二維碼隨處可見,吃個東西、買個青菜,沒有weixin或者zhifubao的掃 ...
  • 昨天寫的這篇博客因為下班時間到了忘記保存了,好鬱悶,得重新寫一遍。實習所在公司使用的是CodeFirst開發模式,最近開始參與到公司的項目裡面來了,發現這個模式特別好用,建庫建表改變欄位屬性添加刪除欄位等等操作都無需自己在資料庫動手操作,只需要編寫代碼即可實現,著實是方便了許多。今天來記錄一下如何使 ...
  • 1 namespace ConsoleApp4 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Mum mum = new Mum(); 8 Dad dad = new Dad(); 9 ... ...
  • (1) 許可權工作流:www.demo.eipflow.com/Account/Login (2) 基礎許可權版:www.auth.eipflow.com/Account/Login (3) Net4.5開源版:http://www.open.eipflow.com/Account/Login 2.Ne ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...