WPF開發(fā)技巧之花式控件功能擴(kuò)展詳解
文章默認(rèn)你已經(jīng)入門WPF了
WPF日常開發(fā),經(jīng)常遇到默認(rèn)的控件功能不滿足需求,怎么辦?
No1. 自定義控件模板
平時開發(fā)中,經(jīng)常遇到比較”俗“的需求,嫌棄控件默認(rèn)的樣子。怎么辦?哈哈,那就整個容唄..... 😜!
還記得心靈深處的Button嗎?是不是第一印象就是規(guī)規(guī)矩矩的長方形,好了,這次我們俗一下,把它變成圓形!
上代碼:
<Button Content="Test1" Width="80" Height="80" FocusVisualStyle="{x:Null}" Background="LightSeaGreen" BorderBrush="DarkBlue"> <Button.Template> <ControlTemplate TargetType="ButtonBase"> <Grid> <Ellipse x:Name="ellipseBorder" StrokeThickness="1" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}"/> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{TemplateBinding Content}"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Stroke" Value="Orange" TargetName="ellipseBorder"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Stroke" Value="OrangeRed" TargetName="ellipseBorder"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Button.Template> </Button>
上外表:
No2. 重寫控件
很多情況下,并不是把控件換個外貌就可以解決的,我們不僅要改變外貌,還要改變控件的功能,比如說我們經(jīng)常用的TextBox控件,正常的功能就是用來輸入的,但更人性化點,我們想要TextBox能告訴我們當(dāng)前的文本框應(yīng)該輸入用戶名呢,還是地址呢等等。其實這個就是我們經(jīng)??吹降乃」δ埽∥淖挚隙ㄒ馨葱柙O(shè)置,那我們不可能簡單的通過改變下控件模板就可以解決的。
通過需求我們知道,新的帶水印的文本框,至少有個水印這么個依賴屬性,供外部設(shè)置。當(dāng)然新的帶水印文本框和TextBox大概的樣子差不多,但我們也要為新的控件定義外貌。
所以第一步,先定義控件的功能,上代碼:
public class WaterMarkTextBox : TextBox { static WaterMarkTextBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(WaterMarkTextBox), new FrameworkPropertyMetadata(typeof(WaterMarkTextBox))); } public string WaterMark { get { return (string)GetValue(WaterMarkProperty); } set { SetValue(WaterMarkProperty, value); } } // Using a DependencyProperty as the backing store for WaterMark. This enables animation, styling, binding, etc... public static readonly DependencyProperty WaterMarkProperty = DependencyProperty.Register("WaterMark", typeof(string), typeof(WaterMarkTextBox), new PropertyMetadata(null)); }
第二步,再定義控件的樣子,上代碼:
<Style TargetType="{x:Type local:WaterMarkTextBox}"> <Setter Property="BorderBrush" Value="Black"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:WaterMarkTextBox}"> <Grid> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> </Border> <ScrollViewer x:Name="PART_ContentHost" Grid.Column="0" Margin="0" Padding="{TemplateBinding Padding}" VerticalAlignment="Stretch" Background="{x:Null}" BorderThickness="0" IsTabStop="False" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> <TextBlock x:Name="PART_Message" Margin="4 0" Padding="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="Center" Foreground="Gray" Text="{TemplateBinding WaterMark}" Visibility="Collapsed" /> </Grid> <ControlTemplate.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value=""> <Setter TargetName="PART_Message" Property="Visibility" Value="Visible" /> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
請注意Name為PART_Message的TextBlock就是用來呈現(xiàn)水印提示消息的,同時加了個觸發(fā)器,實現(xiàn)這樣的功能:如果未輸入任何內(nèi)容,則顯示水印,否則就隱藏水印。
控件使用,上代碼:
<local:WaterMarkTextBox Margin="20,0,0,0" Width="200" Height="50" WaterMark="Please Input your name"/>
上效果:
No3. 附加屬性來試試
重寫控件看似很完美了,真的是這樣嗎?
好了,我的需求又來了,現(xiàn)在文本框提示很perfect,可是我的密碼框PasswordBox也要搞個水印啊?怎么辦?再重寫個帶水印的密碼框?此時有沒有做相同事情的感覺?作為合格的碼農(nóng),我們還是要牢記碼農(nóng)界的警世名言:Don't Repeat Yourself!
此時請回憶下WPF的經(jīng)典知識點:
控件A放到Grid中,A要支持設(shè)置行和列,控件B放到Grid中,B也要支持設(shè)置行和列。教程中已經(jīng)告訴我們不要傻不拉幾在A和B中都去定義行和列的屬性,否則后續(xù)C、D......沒完沒了。
此時就是我們應(yīng)用附加屬性的時候了,在Grid中定義統(tǒng)一的行和列的附加屬性,然后附加應(yīng)用到A、B上就可以了。
反過來看看我們現(xiàn)在的需求,是不是一樣的套路?我是不是在個公共的地方定義個水印WaterMark附加屬性,然后分別應(yīng)用到文本框和密碼框就可以了?說對了一半,因為我們文本框和密碼框老的外表沒有顯示水印的地方,所以我們同時還要重新定義下他們的新外表。
話不多說,先上附加屬性定義的代碼:
public class WaterMarkHelper { public static string GetWaterMark(DependencyObject obj) { return (string)obj.GetValue(WaterMarkProperty); } public static void SetWaterMark(DependencyObject obj, string value) { obj.SetValue(WaterMarkProperty, value); } public static readonly DependencyProperty WaterMarkProperty = DependencyProperty.RegisterAttached("WaterMark", typeof(string), typeof(WaterMarkHelper), new PropertyMetadata(null)); }
上TextBox新的樣式:
<Style x:Key="TextBoxWithWaterMark" TargetType="{x:Type TextBox}"> <Setter Property="BorderBrush" Value="Black"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <Grid> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> </Border> <ScrollViewer x:Name="PART_ContentHost" Grid.Column="0" Margin="0" Padding="{TemplateBinding Padding}" VerticalAlignment="Stretch" Background="{x:Null}" BorderThickness="0" IsTabStop="False" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> <TextBlock x:Name="PART_Message" Margin="4 0" Padding="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="Center" Foreground="Gray" Text="{TemplateBinding local:WaterMarkHelper.WaterMark}" Visibility="Collapsed" /> </Grid> <ControlTemplate.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value=""> <Setter TargetName="PART_Message" Property="Visibility" Value="Visible" /> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
請和自定義控件中的樣式對比下,其實就是PART_Message對應(yīng)控件的Text綁定的不一樣了,這樣綁定的是TextBox的附加屬性WaterMarkHelper.WaterMark
上應(yīng)用代碼:
<TextBox Style="{StaticResource TextBoxWithWaterMark}" Margin="20,0,0,0" Width="200" Height="50" local:WaterMarkHelper.WaterMark="Please Input your name"/>
請注意,這樣要手動明確應(yīng)用定義的樣式資源!
上效果:
課后作業(yè):請依葫蘆畫瓢,實現(xiàn)PasswordBox的水印功能 😆
總結(jié)
除了這里列舉的三種方式,其實還可以通過Behavior行為功能,擴(kuò)展一個控件的功能,比如著名的拖拽功能!寫到這里,我想總結(jié)的是:工欲善其事必先利其器!
當(dāng)我們基礎(chǔ)扎實之后,我們真的可以跳出柵欄,靈活應(yīng)用!
到此這篇關(guān)于WPF日常開發(fā)之花式控件功能擴(kuò)展的文章就介紹到這了,更多相關(guān)WPF花式控件功能擴(kuò)展內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c#winform窗口頁面一打開就加載的實現(xiàn)方式
這篇文章主要介紹了c#winform窗口頁面一打開就加載的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06