【问题标题】:Empty Rows in WPF DataGridWPF DataGrid 中的空行
【发布时间】:2021-05-06 11:08:52
【问题描述】:

我在 wpf UserControl 上有一个 DataGrid 和一个 ComboBox。

namespace ApSap
{ 
public partial class DocumentView : UserControl
{
    public Document document;
    public DocumentView(Document selectedDoc)
    {          
        document = selectedDoc;
        InitializeComponent();
        DocBrowser.Navigate(document.FilePath);
     
        // shows only empty rows
        SapGrid.ItemsSource = document.SapDocNumbers;   
        // shows list of values correctly       
        Combo.ItemsSource = document.SapDocNumbers;
       }
    }
}

组合框正确显示公共属性“SapDocNumbers”(整数列表)的内容,

然而,数据网格只显示空行,尽管它们的数量是正确的。

XAML 如下:

<UserControl x:Class="ApSap.DocumentView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         HorizontalAlignment="Stretch"
         VerticalAlignment="Stretch"
        >
<Grid  VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    
    <StackPanel  Grid.Column="1" Grid.Row="1"> 
        <DataGrid  AutoGenerateColumns="True"  x:Name="SapGrid"   Margin="10,10,10,10" >    
    </DataGrid>
        <Button x:Name="CreateInvoice" Content="Create Invoice"  Margin="10,10,10,10"  />  
        <Button x:Name="Save" Content="Save and Exit"   Margin="10,10,10,10"  />
        <ComboBox x:Name="Combo" Margin="10,10,10,10" />
    </StackPanel>
    


</Grid>

我在 XAML 网格定义中是否遗漏了任何东西,这意味着组合可以正常工作,但数据网格却不能?

这里要求的是类的定义:

public class Document : INotifyPropertyChanged
{
    private int _docID; 

    private List<Int64> _sapDocNumbers;     

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    } 

    public int DocID
    {
        get { return _docID; }
        set
        {
            if (value != _docID)
            {
                _docID = value;
                NotifyPropertyChanged();  
            }
        }
    }  

    public List<Int64> SapDocNumbers
    {
        get { return _sapDocNumbers; }
        set
        {
            if (value != _sapDocNumbers)
            {
                _sapDocNumbers = value;
                NotifyPropertyChanged();
            }
        }
    }

谢谢

【问题讨论】:

    标签: c# wpf data-binding


    【解决方案1】:

    您的问题的答案取决于集合项类型的实现。
    ComboBox默认使用ToString()来表示item。
    DataGrid 呈现元素的属性 - 每个属性都有一个单独的列。
    如果元素类型没有属性,DataGrid 将不会创建列,也不会显示任何内容。

    要获得更准确的答案,请显示集合的类型及其元素类型的实现。

    完成与澄清问题有关的答案:
    您想显示 ulong 集合。
    此类型没有属性,因此在 DataGrid 中自动生成列无法创建列。

    对于您需要的任务:

        <DataGrid  AutoGenerateColumns="False"  x:Name="SapGrid"   Margin="10" >
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Mode=OneWay}" Header="Number"/>
            </DataGrid.Columns>
        </DataGrid>
    

    【讨论】:

    • 我已经添加了类的定义,是你需要的信息吗?
    • 是的。我已经提供了额外的说明。
    • 谢谢,问题已解决。如果我想显示文档类的两个属性(DocId 和列表),我该怎么做? SapGrid.ItemsSource = 文档抛出错误
    • 对于 ItemsSource,需要 IEnumerable 类型:数组、集合、列表等。文档类型为 ONE 实例。而且它没有枚举器。因此,无法将其设置为 ItemsSource 的来源。您将在 DataGrid 的列和行中写下您希望看到的更多详细信息。或许我可以告诉你解决方案。
    • 感谢您的所有帮助,最后一个问题,我如何在 dataGrid 上获得一个空白行,以便用户可以将自己的 sapDocNumbers 添加到列表中?
    【解决方案2】:

    最后一个问题,我如何在 DataGrid 上获得一个空白行,以便用户可以将自己的 SapDocNumbers 添加到列表中?

    1. 集合的项目转到 DataGrid 行的数据上下文。 UI 元素无法编辑其 DataContext。 因此,对于字符串,您需要使用所需类型的属性创建引用 ty 并编辑此属性。 而且这个类型必须有一个默认的构造函数。

    2. 由于列表可以在多个绑定中使用,所以列表的类型不应该是简单的集合,而是可观察的集合。

    例如使用类BaseInpc

    using Simplified;
    
    namespace ApSap
    {
        public class DocumentRow : BaseInpc
        {
            private long _sapDocNumber;
    
            public long SapDocNumber { get => _sapDocNumber; set => Set(ref _sapDocNumber, value); }
    
            public DocumentRow(long sapDocNumber) => SapDocNumber = sapDocNumber;
    
            public DocumentRow() { }
        }
    
    }
    
    using Simplified;
    using System.Collections.ObjectModel;
    
    namespace ApSap
    {
        public class Document : BaseInpc
        {
            private int _docID;
    
            public int DocID { get => _docID; set => Set(ref _docID, value); }
    
            public ObservableCollection<DocumentRow> SapDocNumbers { get; }
                = new ObservableCollection<DocumentRow>();
    
            public Document()
            {
            }
    
            public Document(int docID, params long[] sapDocNumbers)
            {
                DocID = docID;
                foreach (var number in sapDocNumbers)
    
                    SapDocNumbers.Add(new DocumentRow(number));
            }
    
            public static Document ExampleInstance { get; } = new Document(123, 4, 5, 6, 7, 8, 9, 0);
        }
    
    }
    
    <Window x:Class="ApSap.DocumentWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:ApSap"
            mc:Ignorable="d"
            Title="DocumentWindow" Height="450" Width="800"
            DataContext="{x:Static local:Document.ExampleInstance}">
    
        <Grid  VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
    
            <StackPanel  Grid.Column="1" Grid.Row="1">
                <TextBlock Margin="10,10,10,0">
                    <Run Text="DocID:"/>
                    <Run Text="{Binding DocID}"/>
                </TextBlock>
                <DataGrid  AutoGenerateColumns="True"  Margin="10"
                           ItemsSource="{Binding SapDocNumbers}"/>
                <Button x:Name="CreateInvoice" Content="Create Invoice"  Margin="10"  />
                <Button x:Name="Save" Content="Save and Exit"   Margin="10"  />
                <ComboBox x:Name="Combo" Margin="10" />
            </StackPanel>
    
            <DataGrid Grid.Row="1" AutoGenerateColumns="True"  Margin="10"
                      ItemsSource="{Binding SapDocNumbers}" VerticalAlignment="Top"/>
        </Grid>
    </Window>
    

    P.S. 学习设置数据上下文及其绑定。使用 UI 元素的名称,在 Sharp 中引用它们通常是非常糟糕的代码。

    【讨论】:

    • 您好,感谢您的帮助。一些问题: 1. public long SapDocNumber { get => _sapDocNumber;设置 => 设置(参考 _sapDocNumber,值);是因为它在大部分内容上显示错误而简化了吗? 2.没有额外的rows类,是否没有办法修改现有类来处理向列表中添加项目?
    • Set (ref field, value) 方法是 BaseInpc 类的 INotifyPropertyChanged 实现的一部分。我在我的回答中给出了这个类的实现的链接。细看。如果您使用自己的 INPC 实现,请更改属性声明。
    • "нет возможности изменить существующий класс"- 无法通过使用 DataContext 的元素绑定来修改它。 SapDocNumbers 集合的元素被馈送到 DataGrid (DataGrid) 的行上下文中。因此,行内元素的 UI 绑定(并且单元格在一行内)无法更改此元素。因此,如果您需要在 DataGrid 中进行编辑,那么 SapDocNumbers 集合的元素必须是具有属性的类。然后就可以编辑收藏项的属性值了。
    • 同样在这种情况下,触发了 DataGrid 自动生成,这简化了 XALM。并且要向 DataGrid 添加新行,集合项必须具有默认构造函数。出于这个原因,我向您展示了两个构造函数作为示例。您可以删除带参数的构造函数,也可以同时删除两者。但是你不能删除没有参数的构造函数,留下第二个。