【问题标题】:User editable datagrid with empty row, even if observableCollection is empty具有空行的用户可编辑数据网格,即使 observableCollection 为空
【发布时间】:2020-03-07 14:09:35
【问题描述】:

我正在使用数据网格,通过 TwoWay 绑定绑定到 observablecollection。 我的目标是,用户从一个空集合开始生成一个数据列表。 所以我启用了 CanUserAddRow 选项。

在代码中,我使用以下代码生成可观察集合:

private ObservableCollection<Ticket> idlessTicketList = new ObservableCollection<Ticket>();

ObservableCollection 包含的 Ticket 类如下所示:

public class Ticket
{
    public Ticket() { }

    public bool ticketUsed { get; set; }
    public string ticketNumber { get; set; }
    public string ticketCustomer { get; set; }
    public string ticketText { get; set; }
    public double ticketTime { get; set; }
    public Decimal ticketTypeNr { get; set; }
    public string ticketTypeText { get; set; }

}

在 MainWindow 方法中,我将 Datagrid 的 itemSource 设置为 ObservableCollection:

public MainWindow()
{

    InitializeComponent();
    gridIdlessTickets.ItemsSource = idlessTicketList;

}

我现在的问题是,添加新行的空行在启动时不显示。 如果我通过代码 myGridd.Add(row) 添加新行,则空行将正确显示,并且一切正常。

必须如何初始化 ObservableCollection 并正确引用 itemSource? 初始化 itemSource 的最佳位置在哪里?

提前致谢

【问题讨论】:

  • CanUserAddRows="True" 添加到您的DataGrid
  • 我就是这么做的。这有效。但它只有在已经添加了一行时才有效。如果 ObservableCollection 为空,则初始化后,该行不可见。
  • 设置项目源的最佳位置是在 XAML 数据网格中。 ItemsSource="{绑定 idlesTicketList}"。对于数据绑定,您需要一个 DataSource,它可能是本示例中的代码隐藏(通常应该是 ViewModel)。
  • 基本上,如果您希望数据网格中的第一行为空,则需要将项目源设置为可观察集合,将数据源设置为可观察集合所在的类,并在需要实例化的构造函数内部您的可观察集合(作为空集合)。这样(因为有一个空列表)数据网格将在视图显示时在开始时为您提供一个空行。

标签: c# wpf datagrid observablecollection itemsource


【解决方案1】:

这应该适合你。如果有帮助,请告诉我:

XAML:

    <Window>
      <Grid>
        <Datagrid ItemsSource="{Binding idlessTicketList }" SelectionMode="Single" SelectionUnit="Cell" IsReadOnly="False" 
                  CanUserAddRows="True" CanUserDeleteRows="False" AutoGenerateColumns="False">

        //ColumnDefinition...etc
                <DataGridTextColumn Header="TicketNumber" Binding="{Binding TicketNumber}"  />
                <DataGridTextColumn Header="TicketCustomer" Binding="{Binding TicketCustomer}"
        </Datagrid>
<Button name="ThisIsYourUpdateButton" Command="{Binding UpdateMyTicket}" Width="200" Content="Update me now"/>
      </Grid>
    </Window>

代码隐藏 (.xaml.cs):

public MainWindow()
{

    InitializeComponent(); //parses the XAML...
    DataContext = new MainWindowViewModel(); //outsources the view code to the    
    //"controller" to make the view only display and nothing else...

}

ViewModel:(MainWindowViewModel.cs)

        using System;
        using System.Collections.ObjectModel;
        using System.ComponentModel;
        using System.Windows;

        namespace YourNameSpace
        {
            public class MainWindowViewModel : INotifyPropertyChanged
            {
                public ICommand UpdateMyTicket => new DelegateCommand<object>(ExecuteUpdateTicket);
                public Ticket TicketInstance {get;set;}
                public event PropertyChangedEventHandler PropertyChanged;
                public virtual void OnPropertyChanged(string propertyName)
                {
                    if (this.PropertyChanged != null)
                    {
                        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                    }
                }

                private ObservableCollection<Ticket> _idlessTicketList;
                public ObservableCollection<Ticket> idlessTicketList
                {
                    get { return _idlessTicketList; }
                    set
                    {
                        _idlessTicketList = value;
                        OnPropertyChanged("idlessTicketList");
                    }
                }

                //Constructor
                public MainWindowViewModel()
                {
                    idlessTicketList = new ObservableCollection<Ticket>();
                }
                public void ExecuteUpdateTicket(obj param)
                {
                  //if the button is clicked you update your Ticket class properties here!
                }
             }
          }

像这样添加DelegateCommand.cs类:

    using System;

namespace YourNamespaceName 
{ 
    public class DelegateCommand<T> : System.Windows.Input.ICommand
    {
        private readonly Predicate<T> _canExecute;
        private readonly Action<T> _execute;

        public DelegateCommand(Action<T> execute)
            : this(execute, null)
        {
        }

        public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute == null)
                return true;

            return _canExecute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
        }

        public void Execute(object parameter)
        {
            _execute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
        }

        public event EventHandler CanExecuteChanged;
        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }
}

【讨论】:

  • 请注意,INotifyPropertyChanged 在这里非常重要,因为如果用户在视图中进行更改,PropertyChanged 事件将生效并且更改立即生效。您的收藏值(票证属性)将一直在变化。没有这个,视图将无法实时更新。我希望这会有所帮助。
  • @hafisch:现在您唯一需要做的就是当您单击按钮时,属性值将保存到您的 (mysql) 数据库中。这就是您在 ExecuteUpdateTicket 方法中的视图模型中所做的。在您的 ExecuteMethod 中,您只需创建一个 mysql 连接实例并插入或更新您的属性。 TicketModel 中的所有属性都应该映射到您的 mySQL 数据库表结构中(请阅读 Mappers for mysql)。所以你的 TIcket 类基本上是你的 mySQL 表,你的属性是你的 mySQL 列,它的值是你的字段......
  • 不用担心 DelegateCommand.cs:只需使用正确的命名空间将其复制并粘贴到您的项目中。视图模型中的 ICommand 和相应的方法 (ExecuteUpdateTicket) 为您完成其余的工作。它就像一个帮助类,可让您为按钮单击事件添加大量方法......所以您不会像以前那样在 xaml.cs button_click 中执行按钮点击,而是在您的视图模型中!这就是 ICommand 的用途。就是这么轻松和寒冷。
  • 在 XAML 中,我添加了一个带有命令绑定到 vm 的 ICommand 属性的按钮。此 ICommand 属性指的是视图模型中的一个方法,每次单击按钮时都会执行该方法(前提是您添加了具有正确命名空间的 DelegateCommand.cs)。在 vmyou 的 ExecuteUpdateTicket 方法中,添加代码以将更改写入 mysql 数据库(例如)或文本文件,无论您想要什么。您始终可以添加其他按钮和 ICommand 并将它们添加到您的视图模型中。因此,您可以稍后添加一个选择或评估方法,例如绑定到另一个按钮。
  • 如果您不想通过单击按钮而是实时(在键入时)更新数据,那么您可以添加 INotifyPropertyChanged 接口,就像在您的 TICKET.cs 中的视图模型中一样。有时这是必要的。这取决于您的应用程序的需求。也许这对你来说已经足够了。请记住,您将 Datagrid 列绑定到 TicketClasses 的属性。因此,如果列的值发生更改,则 INotifyPropertyChanged 事件会触发,并且您的 observableCollection() 也会更改其值。
【解决方案2】:

使用一个单独的 View-Model 类来初始化 observablecollection,空行可以正确显示。 但是数据不再更新。 这是我所做的:

我将新类添加为新的命名空间

xmlns:vm="clr-namespace:nsMainWindowViewModel"

绑定是这样写的:

<DataGrid ItemsSource="{Binding Path=idlessTicketList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  AutoGenerateColumns="False" >
       <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketCustomer, UpdateSourceTrigger=PropertyChanged}"/>
            <DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketText, UpdateSourceTrigger=PropertyChanged}"/>
            <DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketTime, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:n2\}}"/>
       </DataGrid.Columns>
</Datagrid>

使用此实现,PropertyChangedEventHandler 在应用程序启动时被调用,但元素不会发生变化。

必须如何编写绑定? 对于我尝试过的数据网格:

{Binding Path=(idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
{Binding Path=(vm:idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
{Binding Path=(vm:MainWindowViewModel.idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}

对于我尝试过的列;

{Binding Mode=TwoWay, Path=ticketCustomer, UpdateSourceTrigger=PropertyChanged}
{Binding Mode=TwoWay, Path=(vm:ticketCustomer), UpdateSourceTrigger=PropertyChanged}
{Binding Mode=TwoWay, Path=(vm:MainWindowViewModel.ticketCustomer), UpdateSourceTrigger=PropertyChanged}

通过调试,我可以看到 PropertyChange 方法最初被调用,但不是在编辑单个元素之后。

必须如何定义绑定才能更新另一个命名空间中的 observableCollection?

提前致谢

【讨论】:

  • 您不需要为视图模型添加新的命名空间。只需将您的视图模型用作同一命名空间中的类。将数据网格中的列绑定到 Ticket 类(您的模型)中的属性名称,如下所示: 必须像这样在 MainWindow 构造函数中调整 DataContext: DataContext = new NamespaceName.MainWindowViewModel();您希望如何更新数据?通过按钮单击或在您输入更改时(实时)?
  • 这些天我学到了很多东西。感谢那。解决方案很接近,但我最后一次需要你的帮助。一开始,一个空的第一行是空的网格是可见的,到目前为止一切都很好。如果我通过输入一些文本来编辑字段,则不会调用 PropertyChangedEvent。使用包含“ObsevrableCollection.Add”函数和绑定“gridIdlessTickets.ItemSource = idlessTicketList”的单独方法,一切正常。在这两行之后,其他一切也都有效。直接在网格中创建新行,以及更新单元格内容。可能是什么原因?
  • 您可能需要在您的视图模型中使用票证类属性,如下所示: public Ticket TicketInstance { get;放;然后您的视图模型中也需要这样的 ICommand: public ICommand UpdateMyTicket => new DelegateCommand(ExecuteUpdateTicket);我会更新我的答案,然后你可以试试看是否有效。
猜你喜欢
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多