【WPF】WPF開發用戶控制項、用戶控制項屬性依賴DependencyProperty實現雙向綁定、以及自定義實現Command雙向綁定功能演示

 

 前言:

Wpf開發過程中,最經常使用的功能之一,就是用戶控制項(UserControl)了。用戶控制項可以用於開發用戶自己的控制項進行使用,甚至可以用於打造一套屬於自己的UI框架。依賴屬性(DependencyProperty)是為用戶控制項提供可支援雙向綁定的必備技巧之一,同樣用處也非常廣泛。

以下案例,為了圖方便,我以之前的部落格的基礎為模板,直接進行開發。如有遇到疑問的地方,可以查看先前的部落格(WPF使用prism框架+Unity IOC容器實現MVVM雙向綁定和依賴注入)的文章做個前瞻了解:

//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。通過屬性依賴進行更改圓的顏色,而不是背景色。有興趣的大佬或者需要學習的,可以動手玩一玩,加深印象。

 

以上就是該文章的全部內容,如果對你有幫助,歡迎大佬點贊、留言與轉發。如需轉發,請註明我的部落格出處:

//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);
        }

    }