WPF MVVM 基礎的DataContext Binding

DataContext Binding

https://github.com/momo16542/WPFMVVMTutorial

上面連結為此篇的程式碼,版本為.net framework 4.7.1

在開始之前,可以下載範例,或是自己新增

  1. 新增一個WPF專案,會有一個預設的MainWindow
  2. 先新增兩個資料夾,Model跟ViewModel,此篇著重在ViewModel跟View的Binding。Model可以先忽略
  3. 在ViewModel中新增一個ViewModelBase
public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyname = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
        }
    }
所有的ViewModel都要繼承ViewModelBase,或是實作INotifyPropertyChangedBinding時,調用OnPropertyChanged改變值才會更新View的介面

新增一個MainWindowViewModel

public class MainWindowViewModel : ViewModelBase
    {
        public BookViewModel Book
        {
            get => _book;
            set
            {
                _book = value;
                OnPropertyChanged();
            }
        }

        private BookViewModel _book;

        public ICommand ChangeBookCommand
        {
            get => _changeBookCommand;
            set
            {
                _changeBookCommand = value;
                OnPropertyChanged();
            }
        }

        private ICommand _changeBookCommand;
        public MainWindowViewModel()
        {
            ChangeBookCommand = new BaseCommand(ChangeBookExecute);
            Book = new BookViewModel() { No = 0, Author = "A1000", Title = "A 100 Book" };
        }

        private void ChangeBookExecute()
        {
            var currentNo = Book.No + 1;
            Book = new BookViewModel()
            { No = currentNo, Author = $"A{new Random().Next(100)}", Title = $"A {new Random().Next(100)} Book" };
        }
    }

然後在MainWindow程式碼中實例

		private readonly MainWindowViewModel viewModel;
        public MainWindow()
        {
            InitializeComponent();
            viewModel = new MainWindowViewModel();
            this.DataContext = viewModel;
        }
DataContext是一個對Binding很重要的屬性,它會影響到控件如何在ViewModel中找尋對應的Binding。
DataContext是一個作用範圍,當有明確綁定時,控件會被限制在該範圍內

我們將MainWindow的DataContext設定成MainWindowViewModel,所以在xaml中可以用MainWindowViewModel的屬性名稱去Binding

 				<Grid DataContext="{Binding Book}" Row="0">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Label
                            Grid.Row="0"
                            Grid.Column="0"
                            Margin="3"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Content="No:" />
                        <TextBox
                            x:Name="noTextBox"
                            Grid.Row="0"
                            Grid.Column="1"
                            Width="120"
                            Height="23"
                            Margin="3"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Text="{Binding No, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />

                        <Label
                            Grid.Row="1"
                            Grid.Column="0"
                            Margin="3"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Content="Author:" />
                        <TextBox
                            x:Name="authorTextBox"
                            Grid.Row="1"
                            Grid.Column="1"
                            Width="120"
                            Height="23"
                            Margin="3"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Text="{Binding Author, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
                        <Label
                            Grid.Row="2"
                            Grid.Column="0"
                            Margin="3"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Content="Title:" />
                        <TextBox
                            x:Name="titleTextBox"
                            Grid.Row="2"
                            Grid.Column="1"
                            Width="120"
                            Height="23"
                            Margin="3"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Text="{Binding Title, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
                    </Grid>
                    <Button
                        Grid.Column="1"
                        Width="auto"
                        Height="30"
                        Margin="1"
                        HorizontalAlignment="Left"
                        VerticalAlignment="Top"
                        Command="{Binding ChangeBookCommand}">
                        <TextBlock Margin="3" Text="Change Book" />
                    </Button>

在MainWindow中,左上角的Grid。Button的Command是直接用MainWindowViewModel中的"ChangeBookCommand"
而裡面的Grid的DataContext是設定<Grid DataContext="{Binding Book}" Row="0">,也就是會用MainWindowViewModel 中屬性名稱的"Book"去做Binding,而裡面的TextBox就可以使用BookViewModel裡的屬性名稱去關聯

若是沒有將Grid的DataContext設定成<Grid DataContext="{Binding Book}" Row="0">,那麼裡面的TextBox就要改成這樣Text="{Binding Book.Author " 

有將Grid的DataContext屬性Binding到Book沒有將Grid的DataContext屬性Binding到Book
Text="{Binding AuthorText="{Binding Book.Author
Text="{Binding TitleText="{Binding Book.Title, 

範例中的階層只有兩個

  • MainWindowViewModel 透過程式碼綁定到MainWindow
    • BookViewModel "Book" 綁定到Grid的DataContext,使得Grid裡面的所有控制項,只會在BookViewModel "Book"中找屬性
    • ChangeBookCommand 綁定到Button上,由於Button是在Grid之外,所以會在MainWindowViewModel中尋找綁定屬性

DataContext的功能就是將該控件中的綁定範圍侷限在某一個屬性中,可以減少Binding時的階層輸入。

如果是更多階層的話,可以保持文件的簡潔,尤其是當程式裡可能會重複使用到同一個ViewModel時,可以避免用到預期外的屬性

若是程式執行時,發現不如預期的顯示,可以看visual studio的輸出視窗

有任何改進的意見及問題歡迎傳送到電子郵件

電子郵件:momo16542@gmail.com