首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Windows 8 市集應用程式開發實戰 (55):MVVM 模式下的資料繫結與命令實作

     編輯:關於.NET

前言:關注點分離,打造可測試、可維護的應用程式

Model-View-ViewModel (MVVM) 是 XAML 應用程式的標準架構模式。本文透過完整的產品管理範例(CRUD),示範三層職責劃分:Model 處理資料存取與實體定義,ViewModel 暴露可繫結屬性與 ICommand 行為,View 純 XAML 聲明式繫結,零程式碼後置。

一、Model 層:資料實體與持久化

實體類別 (Product.cs)

[DataContract(Name="product")]
public class Product : INotifyPropertyChanged {
    private int _productId;
    [DataMember(Name="productId", IsRequired=true, Order=1)]
    public int ProductId {
        get { return _productId; }
        set { _productId = value; RaisePropertyChanged("ProductId"); }
    }
    private string _name;
    [DataMember(Name="name", IsRequired=true, Order=2)]
    public string Name {
        get { return _name; }
        set { _name = value; RaisePropertyChanged("Name"); }
    }
    private string _category;
    [DataMember(Name="category", IsRequired=true, Order=3)]
    public string Category {
        get { return _category; }
        set { _category = value; RaisePropertyChanged("Category"); }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string name) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

資料存取類別 (ProductDatabase.cs)

模擬記憶體資料庫,提供查詢、新增、更新、刪除方法:

public class ProductDatabase {
    private List _products;
    public List GetProducts(string name, string category) {
        return _products.Where(p => p.Name.Contains(name) && p.Category.Contains(category)).ToList();
    }
    public Product Add(string name, string category) { /* 新增邏輯 */ }
    public void Update(Product product) { /* 更新邏輯 */ }
    public void Delete(Product product) { /* 刪除邏輯 */ }
}

二、ViewModel 層:狀態與行為的橋樑

主 ViewModel (ProductViewModel.cs)

public class ProductViewModel {
    public ObservableCollection Products { get; set; } // 繫結集合
    public Product Product { get; set; } // 查詢/新增用的暫存物件
    private ProductDatabase _context = new ProductDatabase();

    public ProductViewModel() {
        Products = new ObservableCollection();
        Product = new Product();
    }

    // 查詢命令
    public ICommand GetProductsCommand { get { return new GetProductsCommand(this); } }
    public void GetProducts(Product query) {
        var list = _context.GetProducts(query.Name, query.Category);
        Products.Clear();
        foreach (var p in list) Products.Add(p);
    }

    // 新增命令
    public ICommand AddProductCommand { get { return new AddProductCommand(this); } }
    public void AddProduct(Product product) {
        var newProduct = _context.Add(product.Name, product.Category);
        Products.Insert(0, newProduct);
    }

    // 更新命令
    public ICommand UpdateProductCommand { get { return new UpdateProductCommand(this); } }
    public void UpdateProduct(Product product) {
        product.Name += "U"; product.Category += "U"; // 示範修改
        _context.Update(product);
    }

    // 刪除命令
    public ICommand DeleteProductCommand { get { return new DeleteProductCommand(this); } }
    public void DeleteProduct(Product product) {
        _context.Delete(product);
        Products.Remove(product);
    }
}

命令實作 (ICommand)

每個命令封裝單一行為,CanExecute 控制按鈕啟用狀態:

// 新增命令 - 無參數
public class AddProductCommand : ICommand {
    private ProductViewModel _vm;
    public AddProductCommand(ProductViewModel vm) { _vm = vm; }
    public bool CanExecute(object parameter) => true;
    public event EventHandler CanExecuteChanged;
    public void Execute(object parameter) => _vm.AddProduct(_vm.Product);
}

// 刪除命令 - 需選中項目
public class DeleteProductCommand : ICommand {
    private ProductViewModel _vm;
    public DeleteProductCommand(ProductViewModel vm) { _vm = vm; }
    public bool CanExecute(object parameter) => parameter is Product; // 未選中時按鈕禁用
    public event EventHandler CanExecuteChanged;
    public void Execute(object parameter) => _vm.DeleteProduct((Product)parameter);
}

// 更新命令 - 需選中項目
public class UpdateProductCommand : ICommand { /* 同 DeleteProductCommand */ }

// 查詢命令 - 無參數
public class GetProductsCommand : ICommand { /* 同 AddProductCommand */ }

三、View 層:純 XAML 聲明式繫結

<Page ...>
  <Grid Background="Transparent">
    <StackPanel Margin="120 0 0 0">
      <!-- 設定 DataContext 為 ViewModel -->
      <StackPanel.DataContext>
        <vm:ProductViewModel />
      </StackPanel.DataContext>

      <!-- 列表繫結 -->
      <ListView x:Name="listView" ItemsSource="{Binding Products}" Width="300" Height="300">
        <ListView.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Horizontal">
              <TextBlock Text="{Binding Name}" FontSize="14.667"/>
              <TextBlock Text="{Binding Category}" FontSize="14.667" Margin="10 0 0 0"/>
            </StackPanel>
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>

      <!-- 查詢/新增輸入區 -->
      <StackPanel Orientation="Horizontal" DataContext="{Binding Product}" Margin="0 10 0 0">
        <TextBlock Text="Name:" VerticalAlignment="Center"/>
        <TextBox Text="{Binding Name, Mode=TwoWay}" Width="200"/>
        <TextBlock Text="Category:" VerticalAlignment="Center" Margin="20 0 0 0"/>
        <TextBox Text="{Binding Category, Mode=TwoWay}" Width="200"/>
      </StackPanel>

      <!-- 命令按鈕 -->
      <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
        <Button Content="查詢" Command="{Binding GetProductsCommand}" Margin="10 0 0 0"/>
        <Button Content="新增" Command="{Binding AddProductCommand}" Margin="10 0 0 0"/>
        <Button Content="更新" Command="{Binding UpdateProductCommand}"
                CommandParameter="{Binding SelectedItem, ElementName=listView}" Margin="10 0 0 0"/>
        <Button Content="刪除" Command="{Binding DeleteProductCommand}"
                CommandParameter="{Binding SelectedItem, ElementName=listView}" Margin="10 0 0 0"/>
      </StackPanel>
    </StackPanel>
  </Grid>
</Page>

關鍵繫結技巧

  • ItemsSource="{Binding Products}":ObservableCollection 自動通知 UI 更新
  • DataContext="{Binding Product}":建立子範圍,簡化輸入欄位繫結路徑
  • Mode=TwoWay:輸入控制項雙向繫結
  • CommandParameter="{Binding SelectedItem, ElementName=listView}":將選中項目傳遞給命令
  • CanExecute 回傳 false 時按鈕自動禁用,無需手動控制 IsEnabled

擴充建議:MVVM Light Toolkit

專案若需全域導航服務、訊息傳遞、ViewModel 定位器等進階基礎設施,建議安裝 MVVM Light Toolkit,其 Visual Studio 擴充包含專案範本與程式碼片段,大幅加速開發。

總結與原始碼下載

MVVM 核心在於:View 不知 Model 存在,ViewModel 不知 View 存在,雙向繫結與命令機制實現鬆耦合。本範例展示最精簡的手工實作,理解原理後可引入框架減少樣板程式碼。

完整原始碼下載: https://files.cnblogs.com/webabcd/Windows8.rar

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved