【问题标题】:MVVM : Binding Command with Observable collection to Listbox and taking values from textboxMVVM:将带有 Observable 集合的命令绑定到 Listbox 并从文本框中获取值
【发布时间】:2015-02-25 02:32:16
【问题描述】:

我是 MVVM 的新手,在这个小应用程序中,我有一个列表框、三个文本框和两个按钮,一个是更新,另一个是添加。在 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
    }

}

现在我不知道如何通过单击添加按钮上的命令将值(产品)添加到集合中。

【问题讨论】:

  • 在 ListBox 中,当我添加新条目时,它将使用新条目更新所有可用条目。我正在使用 Observable Collection,这是一个问题吗?

标签: c# wpf mvvm


【解决方案1】:

您可以使用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
}

使用这种类型的命令很容易做你想做的事,例如在你的视图模型中你现在可以这样做:

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。另外,我添加了属性 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 以删除产品的参数。没有必要,您已经在视图模型中选择了 SelectedProduct,但我还是这样做了,以展示如何将参数传递给命令。

[编辑 1]

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

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}" />

【讨论】:

  • 我可以使用这个 RelayCommand 进行添加、更新和删除吗?如果可以,我该如何更新和删除?
  • 更新的逻辑是什么。当您在列表视图中选择一种产品时,您想在文本框中查看其字段以进行编辑吗?
  • 是的……我也想要一个删除按钮来删除当前项目。
  • 嗨@PraveenDeewan,我编辑我的答案。现在您可以执行您想要的三个功能(添加、更新、删除)
  • 文本框不受 AddItem 的限制,这就是为什么当我添加新项目然后 0 时,空白,0 被添加到列表框中并且删除也不起作用
猜你喜欢
  • 2014-07-30
  • 2014-05-04
  • 2012-04-27
  • 1970-01-01
  • 1970-01-01
  • 2014-05-03
  • 1970-01-01
  • 1970-01-01
  • 2012-12-31
相关资源
最近更新 更多