2013-08-08 3 views
3

Я начинаю изучать шаблон WFM MVVM.Как эта кнопка работает в WPF MVVM?

Но я не могу понять, почему этот Button щелчок работает без привязки какого-либо события или действия.

Посмотреть

<Window x:Class="WPF2.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:vm="clr-namespace:WPF2.ViewModel" 
    Title="MainWindow" Height="350" Width="525"> 

    <Window.Resources> 
     <vm:MainWindowViewModel x:Key="MainViewModel" /> 
    </Window.Resources> 

    <Grid x:Name="LayoutRoot" 
      DataContext="{Binding Source={StaticResource MainViewModel}}"> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="*"/> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 

     <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewProducts,Path=SelectedItem.ProductId}" /> 
     <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewProducts,Path=SelectedItem.Name}" /> 
     <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewProducts,Path=SelectedItem.Price}" /> 
     <Label Content="ID" Grid.Row="1" HorizontalAlignment="Left" Margin="12,12,0,274" Name="label1" /> 
     <Label Content="Price" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,59,0,0" Name="label2" VerticalAlignment="Top" /> 
     <Label Content="Name" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,35,0,0" Name="label3" VerticalAlignment="Top" /> 

     <Button Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="141" Height="23" Margin="310,40,0,0" Content="Update" /> 

     <ListView Name="ListViewProducts" Grid.Row="1" Margin="4,109,12,23" ItemsSource="{Binding Path=Products}" > 
      <ListView.View> 
       <GridView x:Name="grdTest"> 
        <GridViewColumn Header="Product ID" DisplayMemberBinding="{Binding ProductId}" Width="100"/> 
        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="250" /> 
        <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" Width="127" /> 
       </GridView> 
      </ListView.View> 
     </ListView> 
    </Grid> 
</Window> 

ViewModel

class MainWindowViewModel : INotifyPropertyChanged 
{ 
    public const string ProductsPropertyName = "Products"; 

    private ObservableCollection<Product> _products; 

    public ObservableCollection<Product> Products 
    { 
     get 
     { 
      return _products; 
     } 
     set 
     { 
      if (_products == value) 
      { 
       return; 
      } 

      _products = value; 
      RaisePropertyChanged(ProductsPropertyName); 
     } 
    } 

    public MainWindowViewModel() 
    { 
     _products = new ObservableCollection<Product> 
     { 
      new Product {ProductId=1, Name="Pro1", Price=11}, 
      new Product {ProductId=2, Name="Pro2", Price=12}, 
      new Product {ProductId=3, Name="Pro3", Price=13}, 
      new Product {ProductId=4, Name="Pro4", Price=14}, 
      new Product {ProductId=5, Name="Pro5", Price=15} 
     }; 
    } 

    #region INotifyPropertyChanged Members 

    public event PropertyChangedEventHandler PropertyChanged; 

    public void RaisePropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    #endregion 
} 

Я нашел этот код из учебника я читал. Когда я нажимаю на строку из списка, устанавливаются значения текстового поля. И когда я редактирую эти значения и нажимаю кнопку, значения в списке отображаются.

На кнопке нет события или команды. Итак, как кнопка click обновляет значения из списка?

+1

Эта кнопка ничего не делает. Этот список заботится о себе. –

+0

Как это? Не могли бы вы объяснить больше? Listview обновляет свое значение при нажатии кнопки. –

+3

Я уверен, что если вы установите фокус в другом месте, список будет обновляться. Это факт, что вы закончили редактирование списка, который заставляет его обновляться, когда он теряет фокус. –

ответ

1

Вам нужно установить UpdateSourceTrigger в Explicit и на Click случае Button вам необходимо обновить источник привязки.

Button s Click Событие выполнено до Command. Следовательно, свойства источника могут быть обновлены в событии Click. Я изменил ваш код, чтобы продемонстрировать это.

Примечание:

  1. Я написал модель представления в коде позади для простоты.
  2. Я использовал RelayCommand из библиотеки MVVMLight.

первый XAML Изменение

<Button Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="141" Height="23" Margin="310,40,0,0" Content="Update" Click="Button_Click" Command="{Binding UpdateCommand}"/> 

второй XAML Изменение

<ListView Name="ListViewProducts" Grid.Row="1" Margin="4,109,12,23" ItemsSource="{Binding Path=Products}" SelectedItem="{Binding SelectedProduct}" > 

код за

using GalaSoft.MvvmLight.Command; 
using System; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Input; 

namespace WPF2 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
      txtID.GetBindingExpression(TextBox.TextProperty).UpdateSource(); 
      txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource(); 
      txtPrice.GetBindingExpression(TextBox.TextProperty).UpdateSource(); 
     } 
    } 

    public class MainWindowViewModel : INotifyPropertyChanged 
    { 
     public const string ProductsPropertyName = "Products"; 

     private ObservableCollection<Product> _products; 

     public ObservableCollection<Product> Products 
     { 
      get 
      { 
       return _products; 
      } 
      set 
      { 
       if (_products == value) 
       { 
        return; 
       } 

       _products = value; 
       RaisePropertyChanged(ProductsPropertyName); 
      } 
     } 

     private Product _SelectedProduct; 

     public Product SelectedProduct 
     { 
      get { return _SelectedProduct; } 
      set 
      { 
       _SelectedProduct = value; 
       RaisePropertyChanged("SelectedProduct"); 
      } 
     } 


     private ICommand _UpdateCommand; 

     public ICommand UpdateCommand 
     { 
      get 
      { 
       if (_UpdateCommand == null) 
       { 
        _UpdateCommand = new RelayCommand(() => 
         { 
          MessageBox.Show(String.Format("From ViewModel:\n\n Updated Product : ID={0}, Name={1}, Price={2}", SelectedProduct.ProductId, SelectedProduct.Name, SelectedProduct.Price)); 
         }); 
       } 
       return _UpdateCommand; 
      } 
     } 


     public MainWindowViewModel() 
     { 
      _products = new ObservableCollection<Product> 
      { 
       new Product {ProductId=1, Name="Pro1", Price=11}, 
       new Product {ProductId=2, Name="Pro2", Price=12}, 
       new Product {ProductId=3, Name="Pro3", Price=13}, 
       new Product {ProductId=4, Name="Pro4", Price=14}, 
       new Product {ProductId=5, Name="Pro5", Price=15} 
      }; 

     } 

     #region INotifyPropertyChanged Members 

     public event PropertyChangedEventHandler PropertyChanged; 

     public void RaisePropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 

     #endregion 
    } 
} 
1

В приведенном выше фрагменте примера ListView привязан к ObservableCollection продукта, который является внутренне реализованным интерфейсом INotfiyPropertyChanged. Этот интерфейс отвечает за повышение значения события PropertyChanged и обновляет привязанные значения элементов пользовательского интерфейса при каждом изменении.

Подробнее о том, что вы можете видеть здесь, свойство Text всех текстовых полей привязано как Text = "{Binding ElementName = ListViewProducts, Path = SelectedItem.PRODUCTID}»

Связывание ElementName - Это расширение разметки, которое покажет XAML компилятор, чтобы связать ваш ListView для управления Textbox

Path - Это указывает на специфическое свойство привязываться элементов пользовательского интерфейса. в этом случае он будет указывать на ListView.SelectedItem свойство объекта, т. е Product.ProductID

. связывание режим элемента WPF UI является ToWay по умолчанию. Таким образом, он будет обновлять бо th Source и Target при изменении значения. Вы можете попробовать это, изменив режим в OneWay

<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewProducts,Path=SelectedItem.ProductId,Mode=OneWay}" /> 
+1

Вместо использования ** bold ** вы можете использовать инструмент «code sample» в режиме редактирования для описания кода. – Default

7

Я знаю, что это старый, но я смотрел в этот сценарий и нашел мой ответ. Во-первых, в теории MVVM «код-сзади» не должен иметь код, все коды которого должны быть в классе ViewModel. Поэтому для того, чтобы осуществить нажатие кнопки, у вас есть этот код в ViewModel (помимо реализации INotifyPropertyChanged):

 public ICommand ButtonCommand { get; set; } 

     public MainWindowViewModel() 
     { 
      ButtonCommand = new RelayCommand(o => MainButtonClick("MainButton")); 
     } 

     private void MainButtonClick(object sender) 
     { 
      MessageBox.Show(sender.ToString());    
     } 

Использование класса:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Input; 

namespace <your namespace>.ViewModels 
{ 
    public class RelayCommand : ICommand 
    { 
     private readonly Action<object> _execute; 
     private readonly Predicate<object> _canExecute; 

     public RelayCommand(Action<object> execute, Predicate<object> canExecute = null) 
     { 
      if (execute == null) throw new ArgumentNullException("execute"); 

      _execute = execute; 
      _canExecute = canExecute; 
     } 

     public bool CanExecute(object parameter) 
     { 
      return _canExecute == null || _canExecute(parameter); 
     } 

     public event EventHandler CanExecuteChanged 
     { 
      add { CommandManager.RequerySuggested += value; } 
      remove { CommandManager.RequerySuggested -= value; } 
     } 

     public void Execute(object parameter) 
     { 
      _execute(parameter ?? "<N/A>"); 
     } 

    } 
} 

И в XAML:

<Window.DataContext> 
    <viewModels:MainWindowViewModel /> 
</Window.DataContext> <Button Content="Button" Command="{Binding ButtonCommand}" /> 
Смежные вопросы