Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何绑定列表<;对象>;在运行时使用MVVM访问DataGrid_C#_Wpf_Data Binding_Mvvm_Datagrid - Fatal编程技术网

C# 如何绑定列表<;对象>;在运行时使用MVVM访问DataGrid

C# 如何绑定列表<;对象>;在运行时使用MVVM访问DataGrid,c#,wpf,data-binding,mvvm,datagrid,C#,Wpf,Data Binding,Mvvm,Datagrid,总之,我有一个视图模型,它使用MVVM绑定到一个DataGrid <DataGrid ItemsSource="{Binding Path=Resources}">...</DataGrid> 所有属性都显示在DataGrid中,但ResourceStringList集合显示为“(集合)” 如何让DataGrid在自己的列中显示ResourceStringList中包含的每个字符串 非常感谢您抽出时间 编辑。我已在下文@Marc中实施了该建议。我现在有以下屏幕截图来说

总之,我有一个视图模型,它使用MVVM绑定到一个
DataGrid

<DataGrid ItemsSource="{Binding Path=Resources}">...</DataGrid>
所有属性都显示在
DataGrid
中,但
ResourceStringList
集合显示为“(集合)”

如何让
DataGrid
在自己的列中显示
ResourceStringList
中包含的每个字符串

非常感谢您抽出时间


编辑。我已在下文@Marc中实施了该建议。我现在有以下屏幕截图来说明我现在需要什么:

“我的资源”列索引3(零索引)前的空白列不是必需的,如何删除此列?

我还想知道如何将列名添加到我的资源列中?也许我可以将
绑定
添加到
SeedColumn
标题
属性中


再次感谢您的时间。

数据网格通常用于显示相同类型的项目列表,每个项目具有固定的属性集,其中每列为一个属性。因此,每行是一项,每列是该项的一个属性。您的情况有所不同,因为没有固定的属性集,而是要显示一个集合,就像它是一个固定的属性集一样

这在很大程度上取决于您是只想显示数据,还是想允许用户操作数据。虽然使用值转换器可以相对容易地实现第一个,但后者需要更多的编码来扩展DataGrid类以允许这种行为。我展示的解决方案是一千种可能性中的两种,可能不是最优雅的。话虽如此,我将描述这两种方式,并从双向版本开始

双向绑定(允许编辑)

我创建了一个自定义的
DataGrid
和一个自定义的“DataGridColumn”,称为“SeedColumn”
SeedColumn
与textcolumn一样工作,但有一个属性
CollectionName
DataGrid
将为您在种子列右侧的
CollectionName
中指定的集合中的每个项目添加一个新的文本列。种子列仅用作一种占位符,用于告诉DataGrid在何处插入哪些列。可以在一个网格中使用多个种子列

轴网和柱类:

public class HorizontalGrid : DataGrid
{
    protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue)
    {
        base.OnItemsSourceChanged(oldValue, newValue);
        foreach (var seed in Columns.OfType<SeedColumn>().ToList())
        { 
            var seedColumnIndex = Columns.IndexOf(seed) + 1;
            var collectionName = seed.CollectionName;
            var headers = seed.Headers;

            // Check if ItemsSource is IEnumerable<object>
            var data = ItemsSource as IEnumerable<object>;
            if (data == null) return;

            // Copy to list to allow for multiple iterations
            var dataList = data.ToList();
            var collections = dataList.Select(d => GetCollection(collectionName, d));
            var maxItems = collections.Max(c => c.Count());

            for (var i = 0; i < maxItems; i++)
            {
                var header = GetHeader(headers, i);
                var columnBinding = new Binding(string.Format("{0}[{1}]" , seed.CollectionName , i));
                Columns.Insert(seedColumnIndex + i, new DataGridTextColumn {Binding = columnBinding, Header = header});
            }
        }
    }

    private static string GetHeader(IList<string> headerList, int index)
    {
        var listIndex = index % headerList.Count;
        return headerList[listIndex];
    }

    private static IEnumerable<object> GetCollection(string collectionName, object collectionHolder)
    {
        // Reflect the property which holds the collection
        var propertyInfo = collectionHolder.GetType().GetProperty(collectionName);
        // Get the property value of the property on the collection holder
        var propertyValue = propertyInfo.GetValue(collectionHolder, null);
        // Cast the value
        var collection = propertyValue as IEnumerable<object>;
        return collection;
    }
}

public class SeedColumn : DataGridTextColumn
{
    public static readonly DependencyProperty CollectionNameProperty =
        DependencyProperty.Register("CollectionName", typeof (string), typeof (SeedColumn), new PropertyMetadata(default(string)));

    public static readonly DependencyProperty HeadersProperty =
        DependencyProperty.Register("Headers", typeof (List<string>), typeof (SeedColumn), new PropertyMetadata(default(List<string>)));

    public List<string> Headers
    {
        get { return (List<string>) GetValue(HeadersProperty); }
        set { SetValue(HeadersProperty, value); }
    }

    public string CollectionName
    {
        get { return (string) GetValue(CollectionNameProperty); }
        set { SetValue(CollectionNameProperty, value); }
    }

    public SeedColumn()
    {
        Headers = new List<string>();
    }
}
添加列标题并不困难,但您需要思考 关于你想从哪里开始。当您存储所有字符串时 在列表中,它们只是字符串,因此与第二个字符串无关 您可以将其用作标题。我已经实现了一种将 只使用XAML的列,现在可能已经足够了:您可以 像这样使用它:

<loc:HorizontalGrid ItemsSource="{Binding Resources}" AutoGenerateColumns="False">
    <loc:HorizontalGrid.Columns>
        <loc:SeedColumn CollectionName="Strings" Binding="{Binding Name}" Header="Name" Visibility="Collapsed">
            <loc:SeedColumn.Headers>
                <system:String>Header1</system:String>
                <system:String>Header2</system:String>
                <system:String>Header3</system:String>
                <system:String>Header4</system:String>
            </loc:SeedColumn.Headers>
        </loc:SeedColumn>
    </loc:HorizontalGrid.Columns>
</loc:HorizontalGrid>
<DataGrid ItemsSource="{Binding Resources, Converter={StaticResource Converter}}" />
然后在XAML中定义一个资源,如下所示,其中loc是您的命名空间:

<loc:ResourceConverter x:Key="Converter" />

然后像这样使用它:

<loc:HorizontalGrid ItemsSource="{Binding Resources}" AutoGenerateColumns="False">
    <loc:HorizontalGrid.Columns>
        <loc:SeedColumn CollectionName="Strings" Binding="{Binding Name}" Header="Name" Visibility="Collapsed">
            <loc:SeedColumn.Headers>
                <system:String>Header1</system:String>
                <system:String>Header2</system:String>
                <system:String>Header3</system:String>
                <system:String>Header4</system:String>
            </loc:SeedColumn.Headers>
        </loc:SeedColumn>
    </loc:HorizontalGrid.Columns>
</loc:HorizontalGrid>
<DataGrid ItemsSource="{Binding Resources, Converter={StaticResource Converter}}" />

我认为对于您的问题没有现成的解决方案,您的网格列必须手动创建。在我的例子中,我在加载
DataGrid
时执行此操作。我假设每个元素的列数是固定的,在我的示例中为10,并且它们的顺序正确:

private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
   var dataGrid = sender as DataGrid;
   dataGrid.Columns.Clear();
   DataGridTextColumn resourceName = new DataGridTextColumn();
   resourceName.Header = "Name";
   resourceName.Binding = new Binding("ResourceName");
   dataGrid.Columns.Add(resourceName);
   for (int i = 0; i < 10; i++)
   {
       var resourceColumn = new DataGridTextColumn();
       resourceColumn.Header = "Resource " + i;
       resourceColumn.Binding = new Binding(String.Format("ResourceStringList[{0}]", i)) { Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
       dataGrid.Columns.Add(resourceColumn);
   }
}
private void DataGrid\u已加载(对象发送方,RoutedEventArgs e)
{
var dataGrid=发送方作为dataGrid;
dataGrid.Columns.Clear();
DataGridTextColumn resourceName=新建DataGridTextColumn();
resourceName.Header=“Name”;
resourceName.Binding=新绑定(“resourceName”);
dataGrid.Columns.Add(resourceName);
对于(int i=0;i<10;i++)
{
var resourceColumn=新的DataGridTextColumn();
resourceColumn.Header=“Resource”+i;
resourceColumn.Binding=新绑定(String.Format(“ResourceStringList[{0}]”,i)){Mode=BindingMode.TwoWay,UpdateSourceTrigger=UpdateSourceTrigger.PropertyChanged};
dataGrid.Columns.Add(resourceColumn);
}
}

下面是一个关于

的简单例子,好吧,那么它的列数是固定的吗?这些专栏里会有什么?他们会有标题吗?如果是,那么他们需要按照特定的顺序吗?@Killercam我很高兴你已经安装并运行到目前为止。我已经编辑了我的答案,以参考您的问题,并上传了我的示例项目。我希望,标题解决方案对您有效?谢谢,我希望它对您有效。我去喝杯咖啡……不用担心,学习WPF是非常困难的,尤其是如果你在自学的话。我也是这么做的,而且花了好几个月的时间,老实说……忘记了:我已经将固定项目上传到了你的skydrive…+1非常感谢你的时间@Marc。如果我想为每一列包含标题,我会怎么做?这不是我想要的。它将资源添加到零索引位置,以列表的形式显示在单个列中。我希望
列表中的每个元素都显示在自己的列中。啊,好的,对不起,我误解了您的意图。。。我会更新我的答案。再次感谢您的时间。我是WPF的新手,在我完全理解它的功能之前,我必须实现您的答案。看我上面的评论,它看起来有点简练,我并不是有意要听起来粗鲁。如果有,我道歉。再次感谢你的帮助…@Killercam:我已经看过了。界面看起来不错,做得不错。我稍微更改了ResourceDataGrid,以便在引发策略的相应事件时进行更新。您应该对此进行一点调整:例如,当前未处理哪些资源发生了更改,因此可能有点效率低下。我希望现在能有所帮助。让我知道。干杯
public class ResourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var resources = value as IEnumerable<ResourceViewModel>;
        if (resources== null) return null;

        // Better play safe and serach for the max count of all items
        var columns = resources[0].ResourceStringList.Count;

        var t = new DataTable();
        t.Columns.Add(new DataColumn("ResourceName"));

        for (var c = 0; c < columns; c++)
        {
            // Will create headers "0", "1", "2", etc. for strings
            t.Columns.Add(new DataColumn(c.ToString()));
        }

        foreach (var r in resources)
        {
            var newRow = t.NewRow();

            newRow[0] = resources.ResourceName;

            for (var c = 0; c < columns; c++)
            {
                newRow[c+1] = r.ResourceStringList[c];
            }

            t.Rows.Add(newRow);
        }


        return t.DefaultView;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
<loc:ResourceConverter x:Key="Converter" />
<DataGrid ItemsSource="{Binding Resources, Converter={StaticResource Converter}}" />
private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
   var dataGrid = sender as DataGrid;
   dataGrid.Columns.Clear();
   DataGridTextColumn resourceName = new DataGridTextColumn();
   resourceName.Header = "Name";
   resourceName.Binding = new Binding("ResourceName");
   dataGrid.Columns.Add(resourceName);
   for (int i = 0; i < 10; i++)
   {
       var resourceColumn = new DataGridTextColumn();
       resourceColumn.Header = "Resource " + i;
       resourceColumn.Binding = new Binding(String.Format("ResourceStringList[{0}]", i)) { Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
       dataGrid.Columns.Add(resourceColumn);
   }
}