前言:
Wpf開發過程中,最經常使用的功能之一,就是使用者控制元件(UserControl)了。使用者控制元件可以用於開發使用者自己的控制元件進行使用,甚至可以用於打造一套屬於自己的UI框架。依賴屬性(DependencyProperty)是為使用者控制元件提供可支援雙向繫結的必備技巧之一,同樣用處也非常廣泛。
以下案例,為了圖方便,我以之前的部落格的基礎為模板,直接進行開發。如有遇到疑問的地方,可以檢視先前的部落格(WPF使用prism框架+Unity IOC容器實現MVVM雙向繫結和依賴注入)的文章做個前瞻瞭解:
https://www.cnblogs.com/weskynet/p/15967764.html
以下是正文(程式碼在文末)
0、配置環境
客戶端環境:WIN 10 專業版
VS開發環境:VS 2022 企業版
執行時環境:.NET 6
開發語言:C#
前端框架:WPF
1、新建了一個使用者控制元件,裡面畫了一個實心圓,以及一個文字控制元件的組合,當作我要實驗使用的使用者控制元件(TestUserControl)。
2、在主窗體裡面進行引用,可以看到引用以後,會在工具箱上顯示新增的使用者控制元件
3、為了測試方便,我直接在先前的Lo'gin頁面直接進行新增該使用者控制元件,效果如下。
4、執行效果如下。由於該使用者控制元件沒有設定過任何屬性,所以現在是沒有任何事件、也沒有辦法更改預設文字等資訊的。
5、接下來進行設定屬性,用於可以直接更改TextName屬性的Text值。設定一個MyText屬性,用於可以獲取和設定使用者控制元件內的TextBlock的Text值。
6、然後可以在Xaml裡面直接通過更改MyText的屬性,來更新顯示的Text值。如下圖所示,設定MyText屬性後,設定值為666,同步更新成666了。
7、但是如果想要實現雙向繫結,其實還不太夠,直接Binding會提示錯誤XDG0062:Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.String'. 如圖。
8、以上問題可以通過自定義依賴屬性來解決。在使用者控制元件的設計器互動程式碼類(TestUserControl)裡面,新增以下程式碼,功能如圖所示。
9、現在在xaml裡面,設定Binding就不會提示錯誤了。
10、並且也可以直接設定值,效果同上面設定屬性以後直接寫值效果一樣。
11、在Login頁面的ViewModel裡面,新增屬性提供給雙向繫結使用。
12、設定MyText進行Binding到剛剛寫的ViewModel的屬性TestText上。
13、執行效果如下圖所示,說明雙向繫結成功了。
14、接下來對使用者控制元件設定單擊事件的雙向繫結。先設定Command有關的依賴屬性。
15、一些有關方法和其他的屬性設定,就不做過多介紹了,看圖說話。
16、然後是關鍵的一步,需要設定單機事件與Command屬性關聯。當然,Command是命名得來的,所以也可以使用其他的命名,也都是OK的,不用在意這些細節,只是預設情況下,單擊都喜歡用Command。如果自帶的控制元件也沒有雙擊、右鍵等雙向繫結,也可以通過設定依賴屬性來實現。
17、在ViewModel裡面定義單擊事件以及有關執行的方法。方法為一個彈出訊息框。
18、使用Command進行繫結事件名稱。
19、執行,並單擊實心圓的效果,並彈出提示框,說明單擊事件通過依賴屬性進行設定成功。
20、接下來測試一下帶引數的事件。在viewmodel裡面,對剛才無引數的事件,改為帶一個string引數的。
21、在xaml裡面,傳入一個字串引數,就叫 Hello world
22、執行,並點選實心圓後效果如圖所示,說明帶引數也是OK的。
23、其他套路如出一轍,大佬們可以自行嘗試,例如通過設定背景依賴屬性,變更實心圓的背景,而不是整個使用者控制元件(正方形)的背景。這部分本來也要寫一個給大佬們壓壓驚,由於時間關係,大佬們可以自己嘗試玩一下。
提示:背景 Background是系統自帶的,所以需要new。通過屬性依賴進行更改圓的顏色,而不是背景色。有興趣的大佬或者需要學習的,可以動手玩一玩,加深印象。
以上就是該文章的全部內容,如果對你有幫助,歡迎大佬點贊、留言與轉發。如需轉發,請註明我的部落格出處:
https://www.cnblogs.com/weskynet/p/16290422.html
如果有知識分享、技術討論的熱情,可通過原文連結(以上部落格園連結為原文連結,CSDN為自動同步連結,其他均為盜版連結) 的文章最下方,點選加入.NET 討論QQ群。或者也可以掃下方我的微信二維碼頭像加我私人微信,然後我可以拉到我的微信交流群一起學習和技術瞭解,也都是OK的,歡迎大佬們來玩。
私人微信:
WeskyNet001
以下是有關最終的原始碼:
TestUserControl:
<Grid> <Viewbox Stretch="Fill"> <Canvas Width="200" Height="200"> <Ellipse Name="rect3" Width="200" Height="200" Stroke="Orange" StrokeThickness="100" > </Ellipse> </Canvas> </Viewbox> <TextBlock x:Name="TextName" Text="123" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock> </Grid>
public partial class TestUserControl : UserControl { public TestUserControl() { InitializeComponent(); } public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText", typeof(String), typeof(TestUserControl), new PropertyMetadata((String)null, new PropertyChangedCallback(TextChanged))); private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TestUserControl control = d as TestUserControl; if (control != null) { String oldText = e.OldValue as String; // 舊的值 String newText = e.NewValue as String; // 更新的新的值 control.UpdateMyText(newText); } } private void UpdateMyText(string newValue) { this.TextName.Text = newValue; } [Bindable(true)] [Category("Appearance")] // using System.ComponentModel; public string MyText { get { return (String)GetValue(MyTextProperty); } set { SetValue(MyTextProperty, value); } } public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(TestUserControl), new PropertyMetadata((ICommand)null, new PropertyChangedCallback(CommandChanged))); public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(TestUserControl)); public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(TestUserControl)); private static void CommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TestUserControl control = d as TestUserControl; if (control != null) { ICommand oldCommand = e.OldValue as ICommand; ICommand newCommand = e.NewValue as ICommand; control.UpdateCommand(oldCommand, newCommand); } } private void UpdateCommand(ICommand oldCommand, ICommand newCommand) { if (oldCommand != null) { oldCommand.CanExecuteChanged -= CanExecuteChanged; } if (newCommand != null) { newCommand.CanExecuteChanged += CanExecuteChanged; } } private void CanExecuteChanged(object sender, EventArgs e) { RoutedCommand command = this.Command as RoutedCommand; if (command != null) { this.IsEnabled = command.CanExecute(CommandParameter, CommandTarget); } else if (this.Command != null) { this.IsEnabled = this.Command.CanExecute(CommandParameter); } } public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public object CommandParameter { get { return GetValue(CommandParameterProperty); } set { SetValue(CommandParameterProperty, value); } } public IInputElement CommandTarget { get { return (IInputElement)GetValue(CommandTargetProperty); } set { SetValue(CommandTargetProperty, value); } } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); RoutedCommand command = Command as RoutedCommand; if (command != null) command.Execute(CommandParameter, CommandTarget); else if (Command != null) this.Command.Execute(CommandParameter); } } }
LoginViewModel:
public class LoginViewModel: BindableBase { public LoginViewModel() { } public string _testText = "999"; public string TextText { get { return _testText; } set { SetProperty(ref _testText, value); } } private DelegateCommand<string> _testCommand; public DelegateCommand<string> TestCommand { get { if (_testCommand == null) { _testCommand = new DelegateCommand<string>(ExecuteTestCommand); } return _testCommand; } } private void ExecuteTestCommand(string value) { MessageBox.Show(value); } }