MVVM:将带有Observable集合的命令绑定到Listbox并从文本框中获取值

时间:2014-12-28 19:23:09

标签: c# wpf mvvm

我是MVVM的新手,在这个小应用程序中,我有一个列表框,三个文本框和两个按钮,一个是Update,另一个是Add。在XAML中我已经完成了所有列表框列与文本框的绑定,根据命令我的更新按钮在我更改任一文本框中的值时正常工作但我不知道如何从文本框中获取值并使用命令在集合中添加值。

这是Xaml代码。

<Grid Height="314">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListView Name="ListViewEmployeeDetails" Grid.Row="1" Margin="4,109,12,23"  ItemsSource="{Binding Products}"  >
        <ListView.View>
            <GridView x:Name="grdTest">
                <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}"  Width="100"/>
                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"  Width="100" />
                <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" Width="100" />
            </GridView>
        </ListView.View>
    </ListView>
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.ID}" />
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewEmployeeDetails,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=ListViewEmployeeDetails,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 Content="Update" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,59,0,0" Name="btnUpdate"
            VerticalAlignment="Top" Width="141"
            Command="{Binding Path=UpdateCommad}"
            />
    <Button Content="Add" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,17,0,0" Name="btnAdd"
        VerticalAlignment="Top" Width="141"
        Command="{Binding UpdateCommad}"
            />
</Grid>

这是产品类

public class Product : INotifyPropertyChanged
{
    private int m_ID;
    private string m_Name;
    private double m_Price;
    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion

    public int ID
    {
        get
        {
            return m_ID;
        }
        set
        {
            m_ID = value;
            OnPropertyChanged("ID");
        }
    }
    public string Name
    {
        get
        {
            return m_Name;
        }
        set
        {
            m_Name = value;
            OnPropertyChanged("Name");
        }
    }
    public double Price
    {
        get
        {
            return m_Price;
        }
        set
        {
            m_Price = value;
            OnPropertyChanged("Price");
        }
    }
}

这里是ViewModel类,现在我将产品静态添加到m_Products中。

class ProductViewModel
{
    private ObservableCollection<Product> m_Products;
    public ProductViewModel()
    {
        m_Products = new ObservableCollection<Product>
    {
        new Product {ID=1, Name ="Pro1", Price=10},
        new Product{ID=2, Name="BAse2", Price=12}
    };
    }
    public ObservableCollection<Product> Products
    {
        get
        {
            return m_Products;
        }
        set
        {
            m_Products = value;
        }
    }
    private ICommand mUpdater;
    public ICommand UpdateCommand
    {
        get
        {
            if (mUpdater == null)
                mUpdater = new Updater();
            return mUpdater;
        }
        set
        {
            mUpdater = value;
        }
    }
    private ICommand addUpdater;
    public ICommand AddCommand
    {
        get 
        {
            if (addUpdater == null)
                addUpdater = new Updater();
            return addUpdater; 
        }
        set
        {
            addUpdater = value;
        }
    }



    private class Updater : ICommand
    {
        #region ICommand Members
        public bool CanExecute(object parameter)
        {
            return true;
        }
        public event EventHandler CanExecuteChanged;
        public void Execute(object parameter)
        {
        }
        #endregion
    }

}

现在我不知道如何使用Add按钮点击命令将值(Product)添加到集合中。

1 个答案:

答案 0 :(得分:2)

您可以使用relay command。它允许您通过传递给其构造函数的委托来注入命令的逻辑:

/// <summary>
/// Class representing a command sent by a button in the UI, defines what to launch when the command is called
/// </summary>
public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

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

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members

    //[DebuggerStepThrough]
    /// <summary>
    /// Defines if the current command can be executed or not
    /// </summary>
    /// <param name="parameter"></param>
    /// <returns></returns>
    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);
    }

    #endregion // ICommand Members
}

使用这种类型的命令可以很容易地执行您想要的操作,例如在您的viewmodel中,您现在可以执行此操作:

public class ProductViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Product> m_Products;
    public ProductViewModel()
    {
        m_Products = new ObservableCollection<Product>
        {
            new Product {ID = 1, Name = "Pro1", Price = 10},
            new Product {ID = 2, Name = "BAse2", Price = 12}
        };
    }


    private Product _selectedProduct;
    public Product SelectedProduct
    {
        get
        {
            return _selectedProduct;
        }
        set
        {
            _selectedProduct = value;
            OnPropertyChanged("SelectedProduct");
        }
    }
    public ObservableCollection<Product> Products
    {
        get
        {
            return m_Products;
        }
        set
        {
            m_Products = value;
        }
    }

    ICommand _addCommand;
    public ICommand AddCommand
    {
        get
        {
            if (_addCommand == null)
            {
                _addCommand = new RelayCommand(param => AddItem());
            }
            return _addCommand;
        }
    }


    ICommand _deleteCommand;
    public ICommand DeleteCommand
    {
        get
        {
            if (_deleteCommand == null)
            {
                _deleteCommand = new RelayCommand(param => DeleteItem((Product)param));
            }
            return _deleteCommand;
        }
    }

    private void DeleteItem(Product product)
    {
        if (m_Products.Contains(product))
        {
            m_Products.Remove(product);
        }
    }

    private void AddItem()
    {
        m_Products.Add(new Product());

    }


    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

如您所见,有两个命令,一个用于添加产品,另一个用于删除所选产品。您不必担心更新,您使用的是ObservableCollection&lt;&gt;。另外,我将属性selectedProduct添加到您的ViewModel以了解您在视图中选择了哪个元素:

    <Grid Height="314">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListView Name="ListViewEmployeeDetails" Grid.Row="1" Margin="4,109,12,23"  ItemsSource="{Binding Products}" SelectedValue="{Binding SelectedProduct}"  >
        <ListView.View>
            <GridView x:Name="grdTest">
                <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}"  Width="100" />
                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"  Width="100" />
                <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" Width="100" />
            </GridView>
        </ListView.View>
    </ListView>
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding SelectedProduct.ID}" />
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding SelectedProduct.Name}" />
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="{Binding SelectedProduct.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 Content="Remove" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,59,0,0" Name="btnUpdate"
        VerticalAlignment="Top" Width="141"
        Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedProduct}"
        />
    <Button Content="Add" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,17,0,0" Name="btnAdd"
    VerticalAlignment="Top" Width="141"
    Command="{Binding AddCommand}"
        />
</Grid>

在删除按钮中,我声明了CommandParamameter,并将其绑定到SelectedProduct属性。这是接收RelayCommand以删除产品的参数。没有必要,您已经在viewmodel中使用了SelectedProduct,但我仍然这样做是为了展示如何将参数传递给命令。

[编辑1]

要实现您想要的行为,您需要在viewModel中添加三个新属性(Id,Name和Price)。现在这些属性应该与TextBoxes绑定。要在ListView中编辑选定的产品,在SelectedProduct属性的集合中,您还需要设置ID,名称和价格属性的值。当文本框也改变其值时,您必须设置所选产品的属性。

ViewModel中的更改:

  private int _id=1;
    public int Id
    {
        get
        {
            return _id;
        }
        set
        {
            _id = value;
            if (SelectedProduct!=null)
            {
                SelectedProduct.ID = _id;
            }
            OnPropertyChanged("Id");
        }
    }

    private string _name;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            if (SelectedProduct != null)
            {
                SelectedProduct.Name = _name;
            }
            OnPropertyChanged("Name");
        }
    }


    private double _price = 0;
    public double Price
    {
        get
        {
            return _price;
        }
        set
        {
            _price = value;
            if (SelectedProduct != null)
            {
                SelectedProduct.Price = _price;
            }
            OnPropertyChanged("Price");
        }
    }

    private Product _selectedProduct;
    public Product SelectedProduct
    {
        get
        {
            return _selectedProduct;
        }
        set
        {
            _selectedProduct = value;

            Id = _selectedProduct != null ? _selectedProduct.ID : 0;
            Name = _selectedProduct != null ? _selectedProduct.Name : "";
            Price = _selectedProduct != null ? _selectedProduct.Price : 0;

            OnPropertyChanged("SelectedProduct");
        }
    }

视图中的更改:

<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding Id}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding Name}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="{Binding Price}" />