【问题标题】:add and retrieve data from datagrid, WPF从数据网格、WPF 添加和检索数据
【发布时间】:2013-03-03 20:50:27
【问题描述】:

我想在按钮单击时将数据添加到数据网格。假设一个数据网格有 3 个标题,即 ITEM、QUANTITY、PRICE。现在,当用户第一次点击时,我会像这样在第一行获取数据。

1   1   1

然后在第二次点击总数据将是

1   1   1
2   2   2

等等

1   1   1
2   2   2
3   3   3
4   4   4
.   .   .
.   .   .
.   .   .
.   .   .
n   n   n

当我单击一个按钮说数组时,我应该在数组列表中获取数据网格数据。这在WPF中可能吗?我已经在使用 jquery 和后端方法的 Web 应用程序中完成了这项工作。无论如何,我希望这在 WPF 中也很容易。我已经搜索过网络,但是所有示例似乎都与数据绑定很复杂,我不想进行数据绑定,并且想采用我在上面尝试解释的简单方式,希望它清楚。

【问题讨论】:

  • 恕我直言DataBinding 是 WPF 和 SL 中最简单的方法。^^
  • 是的,DataBinding 是最简单的方法。否则,每次更改数据时,您都必须编写自己的逻辑来更新数据网格。同样对于数据绑定,这意味着您将拥有一个绑定到数据网格的 itemssource 属性的集合。您也许可以很容易地在集合上调用 ToArray() 或 ToList() 方法来提取结果。

标签: wpf c#-4.0 datagrid wpf-controls wpfdatagrid


【解决方案1】:

我同意这两位评论者的观点,数据绑定是实现这一点的方法,我知道一开始它看起来很复杂,但是一旦你获得了一个很好的转换器和命令库,它就可以一次又一次地重复使用,上面的简单数据绑定示例如下所示。

XAML,创建一个新项目并在主窗口中粘贴此代码,它所做的只是添加一个包含 3 列的数据网格和一个用于添加新行的按钮。请注意,此窗口的数据上下文设置为自身,这意味着 MainWindow 类上的属性将默认使用 {Binding} 公开。

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Content="Button" HorizontalAlignment="Left" Margin="432,289,0,0" VerticalAlignment="Top" Width="75" Command="{Binding AddCommand}"/>
        <DataGrid HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="274" Width="497" AutoGenerateColumns="False" ItemsSource="{Binding Items}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Field1}"/>
                <DataGridTextColumn Binding="{Binding Field2}"/>
                <DataGridTextColumn Binding="{Binding Field3}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

现在是 MainWindow 背后的代码,在这里我创建了 2 个可以绑定到的属性,一个是 Items,其中包含将显示为行的数组,另一个是可以调用以添加另一行的命令.

这里的 ObservableCollection 有一些方法可以在行发生更改时通知绑定引擎,这样您就不必费心自己更新网格了。

 public partial class MainWindow : Window
    {
        public ObservableCollection<Item> Items
        {
            get { return (ObservableCollection<Item>)GetValue(ItemsProperty); }
            set { SetValue(ItemsProperty, value); }
        }
        public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<Item>), typeof(MainWindow), new PropertyMetadata(null));

        public SimpleCommand AddCommand
        {
            get { return (SimpleCommand)GetValue(AddCommandProperty); }
            set { SetValue(AddCommandProperty, value); }
        }
        public static readonly DependencyProperty AddCommandProperty = DependencyProperty.Register("AddCommand", typeof(SimpleCommand), typeof(MainWindow), new PropertyMetadata(null));

        public MainWindow()
        {
            InitializeComponent();
            Items = new ObservableCollection<Item>();
            AddCommand = new SimpleCommand(para =>
            {
                string nextNumber = Items.Count.ToString();
                Items.Add(new Item() { Field1 = nextNumber, Field2 = nextNumber, Field3 = nextNumber });
            });
        }
    }

Item 类,这只是一个非常简单的类,它有 3 个与您的数据网格匹配的属性,field1、2 和 3 这里的依赖属性也意味着您不必在数据更改时自己更新网格.

public class Item : DependencyObject
{
    public string Field1
    {
        get { return (string)GetValue(Field1Property); }
        set { SetValue(Field1Property, value); }
    }
    public static readonly DependencyProperty Field1Property = DependencyProperty.Register("Field1", typeof(string), typeof(Item), new PropertyMetadata(null));

    public string Field2
    {
        get { return (string)GetValue(Field2Property); }
        set { SetValue(Field2Property, value); }
    }
    public static readonly DependencyProperty Field2Property = DependencyProperty.Register("Field2", typeof(string), typeof(Item), new PropertyMetadata(null));

    public string Field3
    {
        get { return (string)GetValue(Field3Property); }
        set { SetValue(Field3Property, value); }
    }
    public static readonly DependencyProperty Field3Property = DependencyProperty.Register("Field3", typeof(string), typeof(Item), new PropertyMetadata(null));
}

最后是命令类,它可以在您的所有项目中反复重用,以将某些内容链接到后面的代码。

public class SimpleCommand : DependencyObject, ICommand
{
    readonly Action<object> _execute;
    readonly Func<object, bool> _canExecute;
    public event EventHandler CanExecuteChanged;

    public SimpleCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _canExecute = canExecute == null ? parmeter => { return true; } : canExecute;
        _execute = execute;
    }
    public virtual void Execute(object parameter)
    {
        _execute(parameter);
    }
    public virtual bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }
}

【讨论】:

  • 对不起,我没有提到mysql,无论如何,我仍然想逃避数据绑定,因为我不想查看数据库中的数据,而是我想在运行时单击按钮手动添加数据,即这些值也可以来自用户输入,稍后在其他按键上我想在那个时间点在数据库中插入数据,直到那时用户应该只能从数据网格中添加或删除数据。希望了解,感谢您的努力
【解决方案2】:

为了展示使用 DataBinding 实现这一目标是多么容易,我快速敲开了这个小应用程序。这花了我大约 10 分钟,但使用 Resharper 和其他工具的更有经验的程序员几乎可以在几分钟内完成。

这是我的 InventoryItemViewModel.cs

public class InventoryItemViewModel : ViewModelBase
{
    private int _itemid;

    public int ItemId
    {
        get { return _itemid; }
        set { _itemid = value; this.OnPropertyChanged("ItemId"); }
    }

    private int _qty;

    public int Qty
    {
        get { return _qty; }
        set { _qty = value; this.OnPropertyChanged("Qty"); }
    }
    private int _price;

    public int Price
    {
        get { return _price; }
        set { _price = value; this.OnPropertyChanged("Price"); }
    }

}

正如您所见,它并没有太多内容,只有您的 3 个属性。实现简单 UI 更新的神奇之处在于我实现了 ViewModelBase。

这里是 ViewModelBase.cs

/// <summary>
/// Abstract base to consolidate common functionality of all ViewModels
/// </summary>
public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

您几乎可以将此类复制到所有 WPF 项目中并按原样使用。

这是我的主窗口的视图模型:MainWindowViewModel.cs

public class MainWindowViewModel : ViewModelBase
{
    public MainWindowViewModel()
    {
        this.InventoryCollection = new ObservableCollection<InventoryItemViewModel>();
        this.AddItemCommand = new DelegateCommand((o) => this.AddItem());
        this.GetItemListCommand = new DelegateCommand((o) => this.GetInventoryItemList());
    }

    public ICommand AddItemCommand { get; private set; }
    public ICommand GetItemListCommand { get; private set; }

    public ObservableCollection<InventoryItemViewModel> InventoryCollection { get; private set; }

    private void AddItem()
    {
        // get maxid in collection
        var maxid = InventoryCollection.Count;
        // if collection is not empty get the max id (which is the same as count in this case but whatever)
        if (maxid > 0) maxid = InventoryCollection.Max(x => x.ItemId);

        InventoryCollection.Add(new InventoryItemViewModel
        {
            ItemId = ++maxid,
            Price = maxid,
            Qty = maxid
        });
    }

    private List<InventoryItemViewModel> GetInventoryItemList()
    {
        return this.InventoryCollection.ToList();
    }
}

如您所见,我有一个 InventoryItemViewModels 的 ObservableCollection。这是我从 UI 绑定到的集合。您必须使用 ObservableCollection 而不是 List 或数组。为了使我的按钮工作,我定义了 ICommand 属性,然后将这些属性绑定到 UI 中的按钮。我使用 DelegateCommand 类将操作重定向到相应的私有方法。

这是 DelegateCommand.cs,这是另一个类,您可以将其包含在 WPF 项目中并相信它可以正常工作。

public class DelegateCommand : ICommand
{
    /// <summary>
    /// Action to be performed when this command is executed
    /// </summary>
    private Action<object> executionAction;

    /// <summary>
    /// Predicate to determine if the command is valid for execution
    /// </summary>
    private Predicate<object> canExecutePredicate;

    /// <summary>
    /// Initializes a new instance of the DelegateCommand class.
    /// The command will always be valid for execution.
    /// </summary>
    /// <param name="execute">The delegate to call on execution</param>
    public DelegateCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Initializes a new instance of the DelegateCommand class.
    /// </summary>
    /// <param name="execute">The delegate to call on execution</param>
    /// <param name="canExecute">The predicate to determine if command is valid for execution</param>
    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this.executionAction = execute;
        this.canExecutePredicate = canExecute;
    }

    /// <summary>
    /// Raised when CanExecute is changed
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    /// <summary>
    /// Executes the delegate backing this DelegateCommand
    /// </summary>
    /// <param name="parameter">parameter to pass to predicate</param>
    /// <returns>True if command is valid for execution</returns>
    public bool CanExecute(object parameter)
    {
        return this.canExecutePredicate == null ? true : this.canExecutePredicate(parameter);
    }

    /// <summary>
    /// Executes the delegate backing this DelegateCommand
    /// </summary>
    /// <param name="parameter">parameter to pass to delegate</param>
    /// <exception cref="InvalidOperationException">Thrown if CanExecute returns false</exception>
    public void Execute(object parameter)
    {
        if (!this.CanExecute(parameter))
        {
            throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute.");
        }
        this.executionAction(parameter);
    }

我的 MainWindow.xaml 上的 UI 代码如下所示:

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

</Window.Resources>

<StackPanel>
    <Button Command="{Binding Path=GetItemListCommand}" Content="Get Item List" />
    <Button Command="{Binding Path=AddItemCommand}" Content="Add Item" />
    <DataGrid ItemsSource="{Binding Path=InventoryCollection}" />
</StackPanel>

为了将它们粘合在一起,我重写了 App.xaml.cs OnStartUp 方法。

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var mainvm = new MainWindowViewModel();
        var window = new MainWindow
        {
            DataContext = mainvm
        };
        window.Show();
    }
}

【讨论】:

  • 猜你是在写我写我的哈哈,但它们几乎一模一样
  • 是的,可能哈哈!我花了很长时间才发布答案,出现了一些疯狂的延迟!
  • 我的数据库是mysql :(,这个可以实现吗
  • 您在关于数据库的问题中没有提到。数据库如何与您的问题交互?
  • 有几种方法可以读取和写入 mysql 数据库中的数据。这是一个显示如何使用实体框架的链接。 weblogs.asp.net/gunnarpeipman/archive/2010/12/09/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-29
  • 2012-12-28
  • 2016-07-11
  • 2016-04-21
  • 2011-01-04
相关资源
最近更新 更多