【问题标题】:How to fix the missing display of rows in a DataGrid, when the columns are dynamically generated?动态生成列时,如何修复 DataGrid 中缺少的行显示?
【发布时间】:2019-08-18 03:08:21
【问题描述】:

在我当前的 WPF 应用程序中,需要在我的 ViewModel 中动态生成 DataGrid 的列。为此,我使用了 System.Windows.Interactivity 和行为的方法。 因此,我创建了 ColumnsBehavior,就像在这篇文章中一样:

Is it possible to get dynamic columns on wpf datagrid in mvvm pattern?

当我初始化有界列表并使用绑定生成列时,一切正常。 当我切换到 ListBox 以在每个 ListBoxItem 中显示一个 DataGrid 时,一切都再次运行良好。 现在我想在运行时将信息(模型)添加到有界列表中。 选择 ListBoxItem 时,应显示相应的 DataGrid。使用 DataGrid 上方的按钮,我可以将示例数据传递给有界列表对象。当我点击 Button 时,会生成正确数量的带有正确标题的列,但这些行仅在下一次出现,其中 ListBoxItem 再次可见。这意味着数据是在我刷新网格时最近出现的。

为了将 Botton Command 属性绑定到 ViewModel,我使用了 ActionCommand 实现:

RelayCommand

我正在使用 MVVM 模式。

这是我的ViewModel

    public class ViewModel
    {
        public ViewModel()
        {
            ListItems = new ObservableCollection<ListItemModel>();
            ListItems.Add(new ListItemModel()
            {
                IsSelected = true
            });
            ListItems.Add(new ListItemModel()
            {
                IsSelected = false
            });

            FillCommand = new RelayCommand(FillAction);
        }

        public ObservableCollection<ListItemModel> ListItems { get; set; }

        public ListItemModel SelectedListItem { get; set; }

        public ICommand FillCommand { get; set; }

        public void FillAction(object sender)
        {
            SelectedListItem.Entries = new ObservableCollection<Entry>()
            {
                new Entry()
                {
                    Cells = new ObservableCollection<Cell>()
                    {
                        new Cell() { Cond1 = 11, Value = 99 },
                        new Cell() { Cond1 = 22, Value = 99 },
                        new Cell() { Cond1 = 33, Value = 99 }
                    }
                },
                new Entry()
                {
                    Cells = new ObservableCollection<Cell>()
                    {
                        new Cell() { Cond1 = 11, Value = 99 },
                        new Cell() { Cond1 = 22, Value = 99 },
                        new Cell() { Cond1 = 33, Value = 99 }
                    },
                },
                new Entry()
                {
                    Cells = new ObservableCollection<Cell>()
                    {
                        new Cell() { Cond1 = 11, Value = 99 },
                        new Cell() { Cond1 = 22, Value = 99 },
                        new Cell() { Cond1 = 33, Value = 99 }
                    },
                }
            };

            SelectedListItem.GenerateGrid();
        }
    }

用于保存列的属性在 ListItemModel 中。 DataGrid 的 ItemsSource 背后的对象结构和 columns 属性你可以在接下来的代码中看到:

public class ListItemModel
    {
        public ListItemModel()
        {
            Entries = new ObservableCollection<Entry>();
            DataColumns = new ObservableCollection<DataGridColumn>();
        }

        public ObservableCollection<Entry> Entries { get; set; }

        public ObservableCollection<DataGridColumn> DataColumns { get; set; }

        public bool IsSelected { get; set; }

        public void GenerateGrid()
        {
            if (Entries.Count != 0)
            {
                var columns = Entries[0]?.Cells;

                for (int i = 0; i < columns.Count; i++)
                {
                    Binding b = new Binding(string.Format("Cells[{0}].Value", i));
                    DataGridTextColumn text = new DataGridTextColumn();
                    text.Header = columns[i].Cond1.ToString();
                    text.Binding = b;
                    DataColumns.Add(text);
                }
            }
        }
    }

    public class Entry
    {
        public ObservableCollection<Cell> Cells { get; set; }

        public Entry() { Cells = new ObservableCollection<Cell>(); }
    }

    public class Cell
    {
        public Cell() { }

        public int Cond1 { get; set; }

        public int Value { get; set; }
    }
}

对于视图,您需要命名空间:

http://schemas.microsoft.com/expression/2010/interactivity

以下代码示例显示了视图。

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <ListBox ItemsSource="{Binding ListItems}" SelectedItem="{Binding SelectedListItem}">
            <ListBox.Resources>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </ListBox.Resources>
        </ListBox>
        <DockPanel Grid.Column="1">
            <Button DockPanel.Dock="Top" Command="{Binding FillCommand}">Fill Data</Button>
            <DataGrid x:Name="ToleranceGrid" ColumnWidth="*" ItemsSource="{Binding Path=SelectedListItem.Entries}"
                      CanUserAddRows="False" SelectionMode="Single" SelectionUnit="Cell" AutoGenerateColumns="False" local:ColumnsBindingBehaviour.BindableColumns="{Binding Path=SelectedListItem.DataColumns}">
            </DataGrid>
        </DockPanel>
    </Grid>

ViewModel 在后面的代码中设置为视图的 DataContext。

结果应如下所示: WPF Application

第一个 ListItemModel 被选中,我按下了 DataGrid 上方的按钮。如您所见,列已生成,但未显示行。我调试了代码,一切都在我的 ViewModel 中正确设置。当我选择第二个 ListItemModel 并返回第一个时,内容将正确显示。

你有什么想法吗?

【问题讨论】:

  • ColumnsBindingBehaviour的实现在哪里?第一个链接是错误的。
  • @mm8 对不起,我编辑了这个问题。谢谢你的建议。

标签: c# wpf data-binding


【解决方案1】:

Entries 属性设置为新集合时,您的ListItemModel 应该实现INotifyPropertyChanged 接口并引发PropertyChanged 事件:

public class ListItemModel : INotifyPropertyChanged
{
    public ListItemModel()
    {
        Entries = new ObservableCollection<Entry>();
        DataColumns = new ObservableCollection<DataGridColumn>();
    }

    private ObservableCollection<Entry> _entries;
    public ObservableCollection<Entry> Entries
    {
        get { return _entries; }
        set { _entries = value; NotifyPropertyChanged(); }
    }

    public ObservableCollection<DataGridColumn> DataColumns { get; set; }

    public bool IsSelected { get; set; }

    public void GenerateGrid()
    {
        if (Entries.Count != 0)
        {
            var columns = Entries[0]?.Cells;

            for (int i = 0; i < columns.Count; i++)
            {
                Binding b = new Binding(string.Format("Cells[{0}].Value", i));
                DataGridTextColumn text = new DataGridTextColumn();
                text.Header = columns[i].Cond1.ToString();
                text.Binding = b;
                DataColumns.Add(text);
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

【讨论】:

  • 我提供了一个非常小的例子,在我的真实代码中,属性的嵌套程度要高得多。我必须在链中的每个 Setter 中将 PropertyChanged 触发到代表属性的行。非常感谢,这花了我将近一周的时间:)
猜你喜欢
  • 2010-12-31
  • 1970-01-01
  • 2012-04-24
  • 2013-12-25
  • 2022-07-14
  • 2013-12-03
  • 1970-01-01
  • 2017-10-23
  • 1970-01-01
相关资源
最近更新 更多