老熟女激烈的高潮_日韩一级黄色录像_亚洲1区2区3区视频_精品少妇一区二区三区在线播放_国产欧美日产久久_午夜福利精品导航凹凸

重慶分公司,新征程啟航

為企業(yè)提供網(wǎng)站建設(shè)、域名注冊、服務(wù)器等服務(wù)

如何進(jìn)行WPF控件編程

這篇文章給大家介紹如何進(jìn)行WPF控件編程,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

創(chuàng)新互聯(lián)專業(yè)提供香港機(jī)房服務(wù)器托管服務(wù),為用戶提供五星數(shù)據(jù)中心、電信、雙線接入解決方案,用戶可自行在線購買香港機(jī)房服務(wù)器托管服務(wù),并享受7*24小時金牌售后服務(wù)。

WPF提供了一系列預(yù)定義組件以供UI開發(fā)人員使用。但軟件開發(fā)人員仍常常需要自行編寫滿足特定要求的控件。以Spinner控件為例,講解如何以派生方式完成自定義控件的編寫。

一.動手前的思考

在著手開始編寫控件之前,我們需要思考Spinner需要以怎樣一種方式實(shí)現(xiàn)?MSDN建議使用三種控件實(shí)現(xiàn)方式:從UserControl類派生,從Control類派生以及從FrameworkElement類派生。

要正確地從這三種方式中作出選擇,軟件開發(fā)人員首先需要了解這些實(shí)現(xiàn)方法的特點(diǎn)。從UserControl類派生和WPF應(yīng)用程序開發(fā)模型非常類似:控件僅僅由現(xiàn)有控件組成,并通過XAML描述。其支持樣式和觸發(fā)器。通過這種方式定義的自定義控件并不希望軟件開發(fā)人員通過模板指定其外觀。

從Control類派生則是大多數(shù)控件開發(fā)所使用的方式。與從UserControl類派生這一方法不同,其外觀并不是由關(guān)聯(lián)的XAML文件指定的,而常常由主題文件所指定。從該類派生的特點(diǎn)有:1) 可以通過ControlTemplate自定義控件的外觀。2) 控件可以支持不同的主題。

而從FrameworkElement類派生則需要徹底拋棄使用控件元素組合的開發(fā)方式(無論是在Template中還是UserControl定義中)。生成基于FrameworkElement的組件有兩種標(biāo)準(zhǔn)方法:直接呈現(xiàn)和自定義元素組合。

直接呈現(xiàn)是指重寫FrameworkElement的OnRender方法,并提供顯式定義組件視覺效果的DrawingContext操作,如Image類和Border類就是通過這種方法定義的。例如精簡后的Border類的OnRender()函數(shù)如下所示:

protectedoverridevoidOnRender(DrawingContext dc)  {  ……  dc.DrawRoundedRectangle(…);  ……  }

另一種則是使用Visual類實(shí)例組合對象外觀。如Track就是使用組合對象外觀的實(shí)例。Track類提供了Thumb屬性,并在其內(nèi)部實(shí)現(xiàn),如ArrangeOverride()函數(shù)中,都考慮了對該組成的使用。

從FrameworkElement中派生的優(yōu)點(diǎn)有:1) 可以完成對控件外觀的精確控制,而不僅僅是簡單的元素組合。2) 通過定義自己的繪制邏輯定義控件的外觀。

很顯然,Spinner控件需要使用從Control類派生的方法,以提供對模板的支持。當(dāng)然,這里并非是指Spinner控件直接從Control類派生,而是選擇Control類的一個派生類作為Spinner的基類。這實(shí)際上與WPF中的控件類型組織特點(diǎn)有關(guān)。WPF中,代表各個控件的類型的繼承層次按照控件特征以非常細(xì)致的方式劃分,并在每個繼承層次中僅添加對一個到兩個特征的支持。就以Button類為例。該類型與Control類之間還存在著兩層派生:ContentControl類以及ButtonBase類。這兩個類型不僅僅分別提供了Content屬性以及命令相關(guān)的屬性,更重要的是,其內(nèi)部提供了支持這些屬性的默認(rèn)實(shí)現(xiàn)。在這種情況下,軟件開發(fā)人員僅僅在默認(rèn)實(shí)現(xiàn)不再滿足條件時才需要更改這些默認(rèn)實(shí)現(xiàn)所提供的邏輯,從而大大減少了開發(fā)新控件所需要的時間。

正是由于這個原因,我們需要在編寫一個控件之前仔細(xì)選擇其所需要使用的基類。選擇一個合適基類的標(biāo)準(zhǔn)就是該類型提供了最多的可重用功能,卻沒有提供過多的冗余功能。而基類的尋找也按照尋找相似控件,沿相似控件的繼承層次由高到低逐個篩選兩步。

在尋找相似控件的時候,軟件開發(fā)人員需要簡單地揣摩一下該控件的使用方法,以尋找具有相似功能的控件。一般情況下,Spinner需要擁有一個***值,一個最小值,并擁有一個當(dāng)前值。軟件開發(fā)人員可以通過Spinner上的按鈕調(diào)整當(dāng)前值的大小,也可以通過輸入框直接輸入當(dāng)前值的大小。這和滾動條控件非常相像,只不過滾動條的直接輸入是通過Thumb完成的。然后我們需要反過來想想,是否ScrollBar提供了過多的Spinner所不需要或不支持的功能。顯然ScrollBar所提供的ViewportSize、Orientation等都不是Spinner所需要的屬性,因此其并不適合作為Spinner的基類。接下來我們可以依次考慮ScrollBar的各個基類,直到選中了一個較為適合的基類為止。就Spinner而言,RangeBase類就是一個較為合適的基類。

這里我們將遇到一個岔路口,那就是是否可以通過僅僅更改現(xiàn)有控件的模板這一方式滿足用戶的需求。如果可以,那么使用自定義模板則是更好的選擇。

在確定需要從某個類派生之后,軟件開發(fā)人員就應(yīng)該檢查該類所提供的各個依賴項(xiàng)屬性所具有的默認(rèn)值是否是一個合理的默認(rèn)值。例如RangeBase類指定了Value屬性的默認(rèn)值為0,最小值為0而***值為1。而對于Spinner而言,由于其常常需要操作整數(shù),因此這些默認(rèn)值都是不適合的。軟件開發(fā)人員需要在類型的靜態(tài)構(gòu)造函數(shù)中對這些默認(rèn)值進(jìn)行重寫:

RangeBase.ValueProperty.OverrideMetadata(typeof(Spinner), newFrameworkPropertyMetadata(10.0, OnValuePropertyChanged));

RangeBase.MaximumProperty.OverrideMetadata(typeof(Spinner), newFrameworkPropertyMetadata(20.0));

RangeBase.LargeChangeProperty.OverrideMetadata(typeof(Spinner), newFrameworkPropertyMetadata(1.0));

RangeBase.SmallChangeProperty.OverrideMetadata(typeof(Spinner), newFrameworkPropertyMetadata(1.0));

需要注意的是,OverrideMetadata()函數(shù)中所提供的屬性默認(rèn)值需要與屬性的類型匹配。例如在為Maximum屬性指定默認(rèn)值時使用整型數(shù)值20,那么對OverrideMetadata()函數(shù)的調(diào)用將導(dǎo)致程序崩潰。

在更改屬性的默認(rèn)值時,軟件開發(fā)人員需要考慮控件所應(yīng)實(shí)際具有的意義。就以ComboBox和ListBox為例。在什么情況下應(yīng)使用ComboBox,而什么情況下應(yīng)使用ListBox呢?回答該問題的決定性因素就是這兩個控件所具有的特征,進(jìn)而導(dǎo)致的用戶體驗(yàn)的區(qū)別。ComboBox可以通過下拉列表顯示所有的可選項(xiàng),并通過編輯框組成顯示當(dāng)前項(xiàng)。這種對數(shù)據(jù)的顯示方式較ListBox占用了更小的空間,并突出顯示了當(dāng)前選中項(xiàng)。而相對于ComboBox,ListBox則在全面展示數(shù)據(jù),尤其是關(guān)聯(lián)型數(shù)據(jù)上較有優(yōu)勢。

同樣的,Spinner也有自己存在的意義。Spinner的中文名稱被稱為微調(diào)控件。從名稱上就可以看出,對Spinner的操作更多的是微小的調(diào)整。同時,Spinner所提供的輸入框常常允許用戶直接輸入需要的數(shù)值,從而達(dá)到對數(shù)值精確的控制。也就是說,相對于ScrollBar等組成,其更注重于對值的精確指定。這也便是我在Spinner中添加精度控制屬性的一個原因。當(dāng)然,該部分內(nèi)容我會在后面繼續(xù)介紹。

在真正開始編寫控件之前,我們還需要考慮的事情就是用戶的使用方法。一般的用戶輸入都是通過鼠標(biāo)和鍵盤來完成的,因此我們就將用戶使用方法歸結(jié)為鼠標(biāo)和鍵盤兩類。

先來看看鼠標(biāo)。鼠標(biāo)需要考慮的主要分為擊鍵和滾輪兩種操作。在鼠標(biāo)左鍵點(diǎn)擊增加及減少按鈕時,數(shù)值需要隨鼠標(biāo)的擊鍵而更改,并提供適當(dāng)?shù)耐庥^反饋。在鼠標(biāo)左鍵點(diǎn)擊輸入框時,光標(biāo)需要移動到相應(yīng)位置。而在鼠標(biāo)右鍵點(diǎn)擊輸入框時,對文本進(jìn)行操作的菜單需要被彈出。在鼠標(biāo)滾輪滾動時,Value的值需要同時進(jìn)行更改。

接下來是鍵盤。一般情況下,鍵盤操作常常與非字符輸入鍵相關(guān)聯(lián)。例如用戶通過Tab等操作導(dǎo)航到控件之后,擁有輸入框組成的控件將自動把其內(nèi)容全部選中。而用戶敲擊Enter鍵則表示他同意當(dāng)前數(shù)值。輸入焦點(diǎn)應(yīng)轉(zhuǎn)移到下一個控件以便用戶繼續(xù)操作。同時對于范圍類型控件而言,Up和Down表示小范圍數(shù)值變化,而PageUp和PageDown則表示大范圍數(shù)值變化。對于Spinner來說,微調(diào)是其主要功能,因此令小范圍數(shù)值變化和大范圍數(shù)值變化的值相等也是合情合理的。

***,在開始編寫控件之前,我們需要借鑒一下WPF中的基于同一基類的類似控件的實(shí)現(xiàn)。有關(guān)如何得到WPF源代碼的方式,請查看“從Dispatcher.PushFrame()說起”一文。通過觀察這些控件的實(shí)現(xiàn),我們可以更好地了解基類所提供的擴(kuò)展點(diǎn)以及這些擴(kuò)展點(diǎn)的使用方法。

二.開始實(shí)現(xiàn)

在本節(jié)中,我們就將開始著手實(shí)現(xiàn)Spinner。右鍵點(diǎn)擊項(xiàng)目文件,并在彈出菜單中選擇“Add”->“New Item”。在彈出的對話框中選擇“Custom Control(WPF)”并在名稱輸入框中輸入“Spinner.cs”,如圖所示:

如何進(jìn)行WPF控件編程

在點(diǎn)擊Add按鈕決定添加控件以后,Visual Studio將為我們添加兩個文件:Spinner.cs以及表示默認(rèn)主題的Generic.xaml。

2.1模板支持

通常情況下,我都會在主題文件中放置控件的一個簡單模板實(shí)現(xiàn)。例如一開始,我在Generic.xaml中為Spinner定義了如下外觀:

                      

雖然這并不是最終的控件外觀,但是通過該控件模板,我們可以隨時測試Spinner控件所包含的邏輯。

在該控件模板定義中,我們?yōu)閹讉€組成提供了特殊的名稱,如PART_Input。在模板定義中,以PART_開頭的名稱表示該名稱所對應(yīng)的組件是在控件內(nèi)部使用的模板定義中必不可少的一部分。為該控件所提供的其它模板同樣需要為這些名稱提供相應(yīng)的組成。

在自定義控件的實(shí)現(xiàn)中,軟件開發(fā)人員需要通過FrameworkTemplate.FindName()函數(shù)從當(dāng)前控件所使用模板的實(shí)例尋找具有特定名稱的組成。使用該函數(shù)的前提條件則是控件的模板已經(jīng)被施行。因此,調(diào)用該函數(shù)的最適合位置就是重載函數(shù)OnApplyTemplate()函數(shù)。如下面代碼所示:

publicoverridevoidOnApplyTemplate()  {  mInputTextBox = null;  mDecreaseButton = null;  mIncreaseButton = null;  base.OnApplyTemplate();  if(Template != null)  {  mInputTextBox = Template.FindName("PART_Input", this) asTextBox;  mDecreaseButton = Template.FindName("PART_Decrease", this) asRepeatButton;  mIncreaseButton = Template.FindName("PART_Increase", this) asRepeatButton;  }  }

為了能讓模板設(shè)計人員能夠知道這些必須在模板定義中出現(xiàn)的名稱以及這些名稱所對應(yīng)的控件類型,WPF提供了TemplatePart特性。該特性提供了兩個屬性Name及Type。Name用來標(biāo)記模板定義中需要添加的組成名稱,而Type則用來指明該名稱所需要具有的類型。如下面代碼所示:

[TemplatePart(Name="PART_Input", Type=typeof(TextBox)),  TemplatePart(Name="PART_Decrease", Type=typeof(RepeatButton)),  TemplatePart(Name="PART_Increase", Type=typeof(RepeatButton))]

在使用了該特性的情況下,模板設(shè)計人員可以直接通過該特性聲明得知模板中所應(yīng)具有的相應(yīng)元素以及其類型。

2.2功能實(shí)現(xiàn)

現(xiàn)在我們就需要考慮如何實(shí)現(xiàn)控件所對應(yīng)的功能。控件與模板之間進(jìn)行互動的方法主要分為兩種:綁定和偵聽模板組成所發(fā)出的事件。在實(shí)現(xiàn)自定義控件時,我們需要盡量使用綁定。但是對于特殊的處理邏輯,我們常常不能通過綁定完成相應(yīng)功能。在這種情況下,軟件開發(fā)人員就需要通過偵聽模板組成所發(fā)出的事件這一方式。

現(xiàn)在就來想想Spinner所需要使用的操作方式:對按鈕控件的輸入可能更改當(dāng)前值,同時在輸入框中執(zhí)行輸入并回車同樣可以確認(rèn)當(dāng)前值。對按鈕的點(diǎn)擊可以觸發(fā)Click事件,更可以觸發(fā)按鈕控件所關(guān)聯(lián)的命令,而在輸入框中敲擊回車鍵則只會觸發(fā)Keydown事件。因此在每次施行模板之后,我們需要為特定組成添加這些處理邏輯:

privatevoidAttach()  {  if(mDecreaseButton != null)  mDecreaseButton.Command = mDecreaseCommand;  ……  if(mInputTextBox != null)  {  mInputTextBox.Text = Value.ToString();  mInputTextBox.InputBindings.Add(newKeyBinding(mIncreaseCommand,  newKeyGesture(Key.Down)));  ……  mInputTextBox.PreviewKeyDown += PreviewTextBoxKeyDown;  mInputTextBox.LostKeyboardFocus += TextBoxLostKeyboardFocus;  }  }

與之對應(yīng)的是,在每次施行模板之前,我們則需要取消這些處理邏輯:

privatevoidDetach()  {  if(mInputTextBox != null)  {  mInputTextBox.PreviewKeyDown -= PreviewTextBoxKeyDown;  mInputTextBox.LostKeyboardFocus -= TextBoxLostKeyboardFocus;  }  }

這是因?yàn)樘砑拥南⑻幚砗瘮?shù)會對消息源生成一個引用。而取消該消息的偵聽則會釋放該引用。

這里,我們來看一下Attach()函數(shù)中所展示的互動方式。

首先是命令。在這里我們使用mDecreaseCommand為按鈕指定命令。為什么使用命令,而不是路由事件?這取決于是否該用戶行為是否需要被用戶知曉。相對于路由事件,路由命令會在遇到相應(yīng)的執(zhí)行邏輯后不繼續(xù)執(zhí)行路由,從而對用戶不可見。

為了支持這些命令,軟件開發(fā)人員需要為Spinner設(shè)置CommandBinding以及InputBinding。CommandBinding為命令指定執(zhí)行邏輯,而InputBinding則為命令指定觸發(fā)命令的執(zhí)行條件。這部分邏輯通常在Spinner的構(gòu)造函數(shù)中完成:

publicSpinner()  {  CommandBindings.Add(newCommandBinding(mIncreaseCommand,  OnIncreaseCommand, CanExecuteIncreaseCommand));  ……  InputBindings.Add(newKeyBinding(mIncreaseCommand, newKeyGesture(Key.Down)));  ……  }

接下來要考慮的則是使用命令之外的另一種處理邏輯,事件。在事件PreviewKeyDown中,我們需要判斷用戶按下的是否是回車鍵。如果是,那么用戶的當(dāng)前輸入將會被驗(yàn)證,并根據(jù)用戶輸入的正確性決定對Value值的刷新。這里存在著幾個需要寫到的問題。首先就是為什么用Preview-事件。TextBox會在處理用戶輸入時將KeyDown事件的handled設(shè)置為true,因此軟件開發(fā)人員不能直接使用KeyDown事件,而是使用PreviewKeyDown事件。另一個則是InputBinding有效的時機(jī)。InputBinding是由KeyDown事件所驅(qū)動的。在KeyDown事件被TextBox處理之前,TextBox實(shí)例內(nèi)設(shè)置的InputBinding將被處理;而在TextBox中,KeyDown事件的handled屬性會在處理過程中被設(shè)置為true,從而使TextBox的各個祖先元素失去了處理InputBinding的機(jī)會。這也便是Attach()函數(shù)為TextBox類型成員mInputTextBox添加額外的InputBinding的原因:

privatevoidAttach()  {  ……  if(mInputTextBox != null)  {  ……  mInputTextBox.InputBindings.Add(newKeyBinding(……));  ……  }  }

接下來,考慮到微調(diào)控件的每次調(diào)整可能并不是整數(shù),因此我們還需要為Spinner提供一種控制顯示精度的方法。這便是添加Precision屬性的原因。該屬性會通過double.ToString()函數(shù)控制當(dāng)前值的格式化執(zhí)行方式,以顯示特定的精度:

privatestringGetValueString()  {  intprecision = Precision <0? 0: Precision;  stringformat = string.Format("F{0}", precision);  returnValue.ToString(format);  }

2.3更改主題

在實(shí)現(xiàn)了所有功能之后,我們就應(yīng)該開始準(zhǔn)備為控件指定主題。

首先要提及的就是如何為控件指定默認(rèn)樣式。為控件指定默認(rèn)樣式的方法主要是通過設(shè)置DefaultStyleKey屬性完成的。完成該工作的最常見方法就是在靜態(tài)構(gòu)造函數(shù)中重寫依賴項(xiàng)屬性DefaultStyleKey的默認(rèn)值:

FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(Spinner), newFrameworkPropertyMetadata(typeof(Spinner)));

接下來,我們就需要在默認(rèn)主題文件Generic.xaml中添加Spinner的外觀定義。該外觀定義的部分代碼如下:

    …    …    …      …        …    …  

如果軟件開發(fā)人員希望為不同的Windows主題提供不同的外觀,那么他可以通過為特定主題提供特定外觀或是偵聽WM_THEMECHANGED事件完成。在需要為特定主題提供特定外觀時,軟件開發(fā)人員需要在項(xiàng)目的Themes文件夾下添加對應(yīng)的主題文件,如Windows經(jīng)典主題對應(yīng)的就是ThemesClassic.xaml。而如果對主題更改的支持是通過偵聽WM_THEMECHANGED事件完成的,那么對控件模板的更換則需要通過代碼顯式完成。

同時,軟件開發(fā)人員還可以通過ThemeInfo特性指定主題文件所存在的位置。該特性擁有兩個和主題相關(guān)的屬性:GenericDictionaryLocation以及ThemeDictionaryLocation。這兩個屬性分別指定了與主題相關(guān)的通用資源所在的位置以及特定于主題的資源所在的位置。它們都接受類型為ResourceDictionaryLocation的枚舉值。該枚舉值中,None表示不使用主題,SourceAssembly表示主題存在于當(dāng)前程序集中,而ExternalAssembly則表示主題字典存在于受主題影響的外部程序集中。

對于本例的示例控件Spinner而言,對ThemeInfo主題的使用如下:

[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]

關(guān)于如何進(jìn)行WPF控件編程就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。


文章名稱:如何進(jìn)行WPF控件編程
URL標(biāo)題:http://www.xueling.net.cn/article/gpedeo.html

其他資訊

在線咨詢
服務(wù)熱線
服務(wù)熱線:028-86922220
TOP
主站蜘蛛池模板: 日日操夜夜透 | 成人福利视频 | 亚洲色无码综合图区手机 | 中文字幕无码热在线 | 成人h动漫无码网站久久 | 凸输偷窥xxxx自由免费视频 | 日本高清视频免费在线观看 | 全免费A级毛片免费看无码 四虎国产精品成人免费久久 | 九九色在线视频 | 亚洲精品国产综合久久一线 | 国产chinesehdxxxx三p | 亚洲精品不卡无码福利在线观看 | 色戒2小时38分无删除版 | 精品久久香蕉国产线看观看亚洲 | 久久久这里有精品999 | 国产成人免费在线 | 美女视频黄a视频全免费网站一区 | 国产一区视频在线 | 国产精品九九久久久久久久 | 97色女 | 精品日韩在线视频 | www.91在线| 成人午夜在线播放 | 国产av无码专区亚洲awww | 日本一区二区三区免费播放视频了 | 宅男一区 | 欧美日在线播放 | 国产日产欧产精品 | 久久久久久久久久久爱 | 91精品国产入口 | 欧美疯狂做受XXXXX高潮 | 无码专区狠狠躁躁天天躁 | 狠狠精品久久久无码中文字幕 | 香港经典a毛片免费观看hd | 今天高清视频免费播放一级爱片 | 无码精品视频一区二区三区 | 精品无码久久午夜福利 | 国产一区欧美日韩精品 | 精品美女久久久久 | 91人人草| 久色免费视频 |