在我們創建界面元素的時候,不管在Vue3+ElementPlus的前端上,還是Winform桌面端上,都是會利用自定義用戶控制項來快速重用一些自定義的界面內容,對自定義用戶控制項的封裝處理,也是我們開發WPF應用需要熟悉的一環。本篇隨筆繼續深入介紹介紹基於CommunityToolkit.Mvvm 和H... ...
在我們創建界面元素的時候,不管在Vue3+ElementPlus的前端上,還是Winform桌面端上,都是會利用自定義用戶控制項來快速重用一些自定義的界面內容,對自定義用戶控制項的封裝處理,也是我們開發WPF應用需要熟悉的一環。本篇隨筆繼續深入介紹介紹基於CommunityToolkit.Mvvm 和HandyControl的WPF應用端開發,主要針對自定義用戶控制項的封裝和使用做一些介紹。
1、自定義用戶控制項的應用場景
在我們使用原生的WPF控制項的時候,有時候發現常規的原生控制項不夠好看,或者功能達不到要求,就需要進行一定程度上的二次封裝處理,也就是自定義控制項的開發場景。
例如我們前面介紹到的用戶信息的查詢界面,我們沒有找到一個輸入數值範圍的控制項,如對於年齡等類似的屬性,我們需要一個區間的查詢處理,可以保留為空,或者最小、最大值之間進行查詢,如下界面所示。
由於WPF沒有這樣的原生控制項,我們需要的話,就需要使用常規的數值或者文本控制項來進行處理,如果多次有這樣的內容,封裝為自定義控制項,讓她簡單的使用,是最為優雅的方式。
我們看到控制項的外觀如下所示。
2、自定義控制項的開發代碼
我們可以用Grid佈局來進行處理,包括兩個TextBlock和兩個文本的控制項界面,我們創建自定義控制項後,在Xaml定義好佈局信息。
<UserControl x:Class="WHC.SugarProject.WpfUI.Controls.NumericRange" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:hc="https://handyorg.github.io/handycontrol" xmlns:local="clr-namespace:WHC.SugarProject.WpfUI.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Name="NumericRangeControl" d:Background="Transparent" d:DesignHeight="32" d:DesignWidth="150" d:Foreground="White" mc:Ignorable="d"> <Grid MinWidth="150"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Margin="10,0,10,0" VerticalAlignment="Center" Text="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}" /> <TextBox x:Name="txtStart" Grid.Column="1" Margin="5" Text="{Binding Path=StartValue, Converter={StaticResource NumericConverter}, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}" /> <TextBlock Grid.Column="2" Margin="5,0,5,0" VerticalAlignment="Center" Text="~" /> <TextBox x:Name="txtEnd" Grid.Column="3" Margin="5" Text="{Binding Path=EndValue, Converter={StaticResource NumericConverter}, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}" /> </Grid> </UserControl>
其中綁定動態屬性的地方,我們使用下麵代碼
Text="{Binding Path=StartValue, Converter={StaticResource NumericConverter}, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}"
當然也可以使用Element的標記方式,這種我們需要設置用戶自定義控制項名稱為Name=“***”,如上面的代碼設置為。
Name="NumericRangeControl"
這樣我們就可以通過自定義控制項的ElementName來定位綁定的屬性了,等同於如下代碼。
<TextBox x:Name="txtStart" Grid.Column="1" Margin="5" Text="{Binding Path=StartValue, Converter={StaticResource NumericConverter}, ElementName=NumericRangeControl}" />
前面我們介紹了該控制項包含了的一些屬性,如StartValue、EndValue、以及文本說明Text等,這些是在用戶控制項後臺代碼裡面進行定義的自定義依賴屬性的,我們來看看代碼。
如我們增加一個StartValue,那麼同時需要增加一個StartValueProperty的自定義依賴屬性。
/// <summary> /// 開始值 /// </summary> public decimal? StartValue { get { return (decimal?)GetValue(StartValueProperty); } set { SetValue(StartValueProperty, value); } } public static readonly DependencyProperty StartValueProperty = DependencyProperty.Register( nameof(StartValue), typeof(decimal?), typeof(NumericRange), new FrameworkPropertyMetadata(default(decimal?), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnStartValuePropertyChanged)));
同時,這個屬性的變化,會觸發一個控制項路由的事件OnStartValuePropertyChanged ,如下所示。
private static void OnStartValuePropertyChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) { if (d is not NumericRange control) return; if (control != null) { var oldValue = (decimal?)e.OldValue; // 舊的值 var newValue = (decimal?)e.NewValue; // 更新的新的值 var args = new RoutedPropertyChangedEventArgs<decimal?>(oldValue, newValue); args.RoutedEvent = NumericRange.ValueChangedEvent; control.RaiseEvent(args); control.ValueChangedCommand?.Execute(null); } }
除了觸發路由事件外,我們可以給該控制項定義一個Command 命令,類似按鈕的命令處理,綁定後就可以接受到相關的通知了。Command的定義如下代碼所示。
/// <summary> /// 數量改變命令 /// </summary> public static readonly DependencyProperty ValueChangedCommandProperty = DependencyProperty.Register("ValueChangedCommand", typeof(ICommand), typeof(NumericRange), new PropertyMetadata(default(ICommand))); /// <summary> /// 數量改變命令 /// </summary> public ICommand ValueChangedCommand { get { return (ICommand)GetValue(ValueChangedCommandProperty); } set { SetValue(ValueChangedCommandProperty, value); } }
3、自定義控制項的使用
自定義控制項開發好後,使用也是很簡單的,需要在頁面或者視窗的定義部分,增加控制項的命名空間,便於引用自定義控制項,如下代碼所示。
xmlns:Controls="clr-namespace:WHC.SugarProject.WpfUI.Controls"
這樣我們在使用的時候,就和其他原生控制項的使用差不多了。如下是在頁面中使用的Xaml代碼。
<Controls:NumericRange EndValue="{Binding ViewModel.PageDto.AgeEnd, UpdateSourceTrigger=PropertyChanged}" StartValue="{Binding ViewModel.PageDto.AgeStart, UpdateSourceTrigger=PropertyChanged}" Text="年齡" ValueChangedCommand="{Binding ViewModel.SearchCommand}" />
我們可以看到自定義控制項的屬性的綁定,和其他控制項的屬性綁定一致的,而且我們這裡定義了一個Command:ValueChangedCommand
我們可以通過這個命令接收控制項變化的通知。這樣就可以正常的實現我們所需要的處理功能了。
另外,自定義控制項的輸入框,一般會在失去焦點後觸發命令處理,我們也可以讓文本輸入框在輸入後回車觸發命令處理,我們增加一個KeyDown的事件處理,如下代碼所示。
<TextBox x:Name="txtStart" Grid.Column="1" Margin="5" KeyDown="txtStartEndValue_KeyDown" Text="{Binding Path=StartValue, Converter={StaticResource NumericConverter}, ElementName=NumericRangeControl}" /> <TextBlock Grid.Column="2" Margin="5,0,5,0" VerticalAlignment="Center" Text="~" /> <TextBox x:Name="txtEnd" Grid.Column="3" Margin="5" KeyDown="txtStartEndValue_KeyDown" Text="{Binding Path=EndValue, Converter={StaticResource NumericConverter}, RelativeSource={RelativeSource AncestorType={x:Type local:NumericRange}}}" />
讓回車切換到下一個焦點即可。
private void txtStartEndValue_KeyDown(object sender, KeyEventArgs e) { if(e.Key == Key.Enter) { var textBox = sender as System.Windows.Controls.TextBox; if(textBox != null) { //切換焦點會觸發值更新命令 textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); } } }
至此我們就完成了完美的控制項處理事件了。
編譯後,我們就可以在工具欄中看到用戶自定義控制項的列表了,可以直接拖動它到頁面進行使用。
至此,我們就實現了自定義控制項在頁面上的使用了,非常簡單。
當然,我們也可以組合一些面板,來實現更加複雜的控制項呈現方式,可以設計一些圖表、文本內容的綜合展示,如下是其中的一個控制項的多層展示。
根據不同的圖標、內容,背景色、以及一些集合形狀的疊加,就可以設計出非常好看的單個用戶控制項,然後動態設置,就可以很好的實現不同的內容展示。
專註於代碼生成工具、.Net/.NetCore 框架架構及軟體開發,以及各種Vue.js的前端技術應用。著有Winform開發框架/混合式開發框架、微信開發框架、Bootstrap開發框架、ABP開發框架、SqlSugar開發框架等框架產品。
轉載請註明出處:撰寫人:伍華聰 http://www.iqidi.com