wpf 如何将行添加到DataGrid以立即显示它?

tnkciper  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(86)

我试图在C# WPF MVVM中单击DataGrid中的按钮后添加另一行。问题是,只要我单击其他位置(例如在TextBox中),该行就会被添加。这意味着DataGrid在单击“添加行”按钮后不会立即更新。

NewDocumentView.xaml

<DataGrid x:Name="DataGrid"
          AutoGenerateColumns="False"
          HorizontalAlignment="Left"
          VerticalAlignment="Top"
          Grid.Row="1"
          Margin="20,350,0,0"
          ItemsSource="{Binding Items}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Description" Binding="{Binding Description}" Width="500"/>
        <DataGridTextColumn Header="Unit" Binding="{Binding Unit}" Width="100"/>
        <DataGridTextColumn Header="Price" Binding="{Binding Price}" Width="100"/>
        <DataGridTextColumn Header="Total" Binding="{Binding Total}" Width="100"/>
        <DataGridTemplateColumn Header="Actions">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="Add Row" Command="{Binding AddItemCommand}" Width="100"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

字符串

NewDocumentViewModel.cs

namespace App.MVVM.ViewModel
{
    internal class NewDocumentViewModel : ObservableObject
    {
        public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<Item>();

        public RelayCommand AddItemCommand { get; set; }

        public NewDocumentViewModel()
        {
            Items = new ObservableCollection<Item>();

            AddItemCommand = new RelayCommand(o => { AddNewItem(); });
        }

        private void AddNewItem()
        {
            Items.Add(new Item());
        }
    }
}

NewDocumentView.xaml.cs

namespace App.MVVM.View
{
    public partial class NewDocumentView : UserControl
    {
        public NewDocumentView()
        {
            InitializeComponent();
            DataContext = new NewDocumentViewModel();
        }
    }
}

Item.cs

namespace App.MVVM.Model
{
    class Item : ObservableObject
    {
        private string _description;
        public string Description
        {
            get { return _description; }
            set
            {
                if (_description != value)
                {
                    _description = value;
                    OnPropertyChanged(nameof(Description));
                }
            }
        }

        private string _unit;
        public string Unit
        {
            get { return _unit; }
            set
            {
                if (_unit != value)
                {
                    _unit = value;
                    OnPropertyChanged(nameof(Unit));
                }
            }
        }

        private int _price;
        public int Price
        {
            get { return _price; }
            set
            {
                if (_price != value)
                {
                    _price = value;
                    OnPropertyChanged(nameof(Price));
                }
            }
        }

        private int _total;
        public int Total
        {
            get { return _total; }
            set
            {
                if (_total != value)
                {
                    _total = value;
                    OnPropertyChanged(nameof(Total));
                }
            }
        }
    }
}

ObservableObject.cs

namespace App.Core
{
    internal class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

RelayCommand.cs

namespace App.Core
{
    internal class RelayCommand : ICommand
    {
        private readonly Action<object> execute;
        private readonly Func<object, bool> canExecute;

        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }

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

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

        public void Execute(object parameter)
        {
            execute(parameter);
        }
    }
}


我做错了什么?我如何使用按钮立即更新DataGrid向DataGrid添加新行?

2nbm6dog

2nbm6dog1#

当你将ObservableCollection绑定到datagrid的Itemssource时,集合中的每个Item都被模板化到一行中。每行的文本都是一个项。
绑定时,它会在Item ontext中查找属性。绑定到AddItemCommand会完全失败,因为Item没有AddItemCommand属性。
您可以使用相对搜索在可视树中搜索给定类型https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/relativesource-markupextension?view=netframeworkdesktop-4.8
如果你这样做是为了找到datagrid,那么它的datagontext是从NewDocumentView继承的,并且是NewDocumentViewModel。当然,这是你的命令所在的位置。
但是,您需要显式地告诉它使用DataContext上的属性,因为否则它将查看DatagridUI控件。
这里有一个简单的例子,我使用mvvm community toolkit来说明这一点,这个代码是快速的和肮脏的,只是为了说明这一点。
MainWindow

<Window.DataContext>
    <local:MainWindowViewmodel/>
</Window.DataContext>
<Grid>
    <DataGrid ItemsSource="{Binding Items}">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Actions">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Content="Add Row" 
Command="{Binding DataContext.AddItemCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}" Width="100"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

字符串
请注意,与相对对象绑定,我使用的是DataContext.AddItemCommand
视图模型:

public partial class MainWindowViewmodel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<string> items = new ObservableCollection<string>{"A", "B" };
    
    [RelayCommand]
    private async Task AddItem()
    {
        Items.Add("X");
    }
}


当我单击该按钮时,将添加另一行。
如果你感兴趣,可能是一个想法,搜索和阅读适当的文章的工具包,但简短:
该工具包使用代码生成器在分部类中生成Items属性和AddItemCommand。

相关问题