C# 如何绑定列表<;对象>;在运行时使用MVVM访问DataGrid
总之,我有一个视图模型,它使用MVVM绑定到一个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中实施了该建议。我现在有以下屏幕截图来说
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);
}
}