WPF DataGrid를 가변 개수의 열에 어떻게 바인딩합니까?
내 WPF 응용 프로그램은 매번 다른 열 수를 가질 수있는 데이터 집합을 생성합니다. 출력에는 형식을 적용하는 데 사용될 각 열에 대한 설명이 포함됩니다. 출력의 단순화 된 버전은 다음과 같습니다.
class Data
{
IList<ColumnDescription> ColumnDescriptions { get; set; }
string[][] Rows { get; set; }
}
이 클래스는 WPF DataGrid에서 DataContext로 설정되었지만 실제로 프로그래밍 방식으로 열을 만듭니다.
for (int i = 0; i < data.ColumnDescriptions.Count; i++)
{
dataGrid.Columns.Add(new DataGridTextColumn
{
Header = data.ColumnDescriptions[i].Name,
Binding = new Binding(string.Format("[{0}]", i))
});
}
대신이 코드를 XAML 파일의 데이터 바인딩으로 바꾸는 방법이 있습니까?
다음은 DataGrid에서 열 바인딩에 대한 해결 방법입니다. Columns 속성은 ReadOnly이므로 모든 사람이 알듯이 BindableColumns라는 Attached 속성을 만들었습니다. Collectiond 컬렉션 이벤트를 통해 컬렉션이 변경 될 때마다 DataGrid의 열을 업데이트합니다.
이 DataGridColumn 컬렉션이 있다면
public ObservableCollection<DataGridColumn> ColumnCollection
{
get;
private set;
}
그런 다음 BindableColumns를 다음과 같이 ColumnCollection에 바인딩 할 수 있습니다.
<DataGrid Name="dataGrid"
local:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}"
AutoGenerateColumns="False"
...>
연결된 속성 바인딩 가능 열
public class DataGridColumnsBehavior
{
public static readonly DependencyProperty BindableColumnsProperty =
DependencyProperty.RegisterAttached("BindableColumns",
typeof(ObservableCollection<DataGridColumn>),
typeof(DataGridColumnsBehavior),
new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = source as DataGrid;
ObservableCollection<DataGridColumn> columns = e.NewValue as ObservableCollection<DataGridColumn>;
dataGrid.Columns.Clear();
if (columns == null)
{
return;
}
foreach (DataGridColumn column in columns)
{
dataGrid.Columns.Add(column);
}
columns.CollectionChanged += (sender, e2) =>
{
NotifyCollectionChangedEventArgs ne = e2 as NotifyCollectionChangedEventArgs;
if (ne.Action == NotifyCollectionChangedAction.Reset)
{
dataGrid.Columns.Clear();
foreach (DataGridColumn column in ne.NewItems)
{
dataGrid.Columns.Add(column);
}
}
else if (ne.Action == NotifyCollectionChangedAction.Add)
{
foreach (DataGridColumn column in ne.NewItems)
{
dataGrid.Columns.Add(column);
}
}
else if (ne.Action == NotifyCollectionChangedAction.Move)
{
dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
}
else if (ne.Action == NotifyCollectionChangedAction.Remove)
{
foreach (DataGridColumn column in ne.OldItems)
{
dataGrid.Columns.Remove(column);
}
}
else if (ne.Action == NotifyCollectionChangedAction.Replace)
{
dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
}
};
}
public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
{
element.SetValue(BindableColumnsProperty, value);
}
public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
{
return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
}
}
나는 나의 연구를 계속했고 이것을하는 합리적인 방법을 찾지 못했다. DataGrid의 Columns 속성은 바인딩 할 수있는 것이 아니며 실제로 읽기 전용입니다.
Bryan은 AutoGenerateColumns로 무언가를 할 수 있다고 제안했습니다. 간단한 .Net 리플렉션을 사용하여 ItemsSource에있는 객체의 속성을보고 각각에 대한 열을 생성합니다. 아마도 각 열의 속성을 사용하여 즉시 유형을 생성 할 수는 있지만 궤도에서 벗어나고 있습니다.
이 문제는 코드에서 쉽게 해결되므로 데이터 컨텍스트가 새 열로 업데이트 될 때마다 호출하는 간단한 확장 메서드를 사용합니다.
public static void GenerateColumns(this DataGrid dataGrid, IEnumerable<ColumnSchema> columns)
{
dataGrid.Columns.Clear();
int index = 0;
foreach (var column in columns)
{
dataGrid.Columns.Add(new DataGridTextColumn
{
Header = column.Name,
Binding = new Binding(string.Format("[{0}]", index++))
});
}
}
// E.g. myGrid.GenerateColumns(schema);
Deborah Kurata의 블로그 기사에서 DataGrid에 가변 개수의 열을 표시하는 방법을 소개했습니다.
MVVM을 사용하여 Silverlight 응용 프로그램에서 동적 열이있는 데이터 그리드 채우기
기본적으로 그녀 는 여러 열을 표시하는 내부를 작성하고 내부에 DataGridTemplateColumn
넣습니다 ItemsControl
.
다음과 같은 코드 줄을 사용하여 열을 동적으로 추가 할 수있었습니다.
MyItemsCollection.AddPropertyDescriptor(
new DynamicPropertyDescriptor<User, int>("Age", x => x.Age));
질문과 관련하여 이것은 XAML 기반 솔루션이 아니며 (해당 방법이 없기 때문에) DataGrid.Columns와 직접 작동하는 솔루션도 아닙니다. 실제로는 ITypedList를 구현하고 PropertyDescriptor 검색을위한 사용자 정의 메소드를 제공하는 DataGrid 바운드 ItemsSource와 함께 작동합니다. 코드의 한 곳에서 그리드의 "데이터 행"및 "데이터 열"을 정의 할 수 있습니다.
당신이 가지고 있다면 :
IList<string> ColumnNames { get; set; }
//dict.key is column name, dict.value is value
Dictionary<string, string> Rows { get; set; }
예를 들어 사용할 수 있습니다.
var descriptors= new List<PropertyDescriptor>();
//retrieve column name from preprepared list or retrieve from one of the items in dictionary
foreach(var columnName in ColumnNames)
descriptors.Add(new DynamicPropertyDescriptor<Dictionary, string>(ColumnName, x => x[columnName]))
MyItemsCollection = new DynamicDataGridSource(Rows, descriptors)
MyItemsCollection에 바인딩을 사용하는 그리드는 해당 열로 채워집니다. 이러한 열은 런타임에 동적으로 수정 (신규 추가 또는 기존 제거) 될 수 있으며 그리드는 자동으로 열 수집을 새로 고칩니다.
위에서 언급 한 DynamicPropertyDescriptor는 일반 PropertyDescriptor 로의 업그레이드 일 뿐이며 몇 가지 추가 옵션이있는 강력한 형식의 열 정의를 제공합니다. 그렇지 않으면 DynamicDataGridSource는 기본 PropertyDescriptor와 함께 잘 작동합니다.
탈퇴를 처리하는 허용되는 답변의 버전을 만들었습니다.
public class DataGridColumnsBehavior
{
public static readonly DependencyProperty BindableColumnsProperty =
DependencyProperty.RegisterAttached("BindableColumns",
typeof(ObservableCollection<DataGridColumn>),
typeof(DataGridColumnsBehavior),
new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
/// <summary>Collection to store collection change handlers - to be able to unsubscribe later.</summary>
private static readonly Dictionary<DataGrid, NotifyCollectionChangedEventHandler> _handlers;
static DataGridColumnsBehavior()
{
_handlers = new Dictionary<DataGrid, NotifyCollectionChangedEventHandler>();
}
private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = source as DataGrid;
ObservableCollection<DataGridColumn> oldColumns = e.OldValue as ObservableCollection<DataGridColumn>;
if (oldColumns != null)
{
// Remove all columns.
dataGrid.Columns.Clear();
// Unsubscribe from old collection.
NotifyCollectionChangedEventHandler h;
if (_handlers.TryGetValue(dataGrid, out h))
{
oldColumns.CollectionChanged -= h;
_handlers.Remove(dataGrid);
}
}
ObservableCollection<DataGridColumn> newColumns = e.NewValue as ObservableCollection<DataGridColumn>;
dataGrid.Columns.Clear();
if (newColumns != null)
{
// Add columns from this source.
foreach (DataGridColumn column in newColumns)
dataGrid.Columns.Add(column);
// Subscribe to future changes.
NotifyCollectionChangedEventHandler h = (_, ne) => OnCollectionChanged(ne, dataGrid);
_handlers[dataGrid] = h;
newColumns.CollectionChanged += h;
}
}
static void OnCollectionChanged(NotifyCollectionChangedEventArgs ne, DataGrid dataGrid)
{
switch (ne.Action)
{
case NotifyCollectionChangedAction.Reset:
dataGrid.Columns.Clear();
foreach (DataGridColumn column in ne.NewItems)
dataGrid.Columns.Add(column);
break;
case NotifyCollectionChangedAction.Add:
foreach (DataGridColumn column in ne.NewItems)
dataGrid.Columns.Add(column);
break;
case NotifyCollectionChangedAction.Move:
dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
break;
case NotifyCollectionChangedAction.Remove:
foreach (DataGridColumn column in ne.OldItems)
dataGrid.Columns.Remove(column);
break;
case NotifyCollectionChangedAction.Replace:
dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
break;
}
}
public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
{
element.SetValue(BindableColumnsProperty, value);
}
public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
{
return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
}
}
그리드 정의를 사용하여 usercontrol을 만들고 xaml에서 다양한 열 정의를 사용하여 '자식'컨트롤을 정의 할 수 있습니다. 부모는 열에 대한 종속성 속성과 열을로드하는 방법이 필요합니다.
부모의:
public ObservableCollection<DataGridColumn> gridColumns
{
get
{
return (ObservableCollection<DataGridColumn>)GetValue(ColumnsProperty);
}
set
{
SetValue(ColumnsProperty, value);
}
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register("gridColumns",
typeof(ObservableCollection<DataGridColumn>),
typeof(parentControl),
new PropertyMetadata(new ObservableCollection<DataGridColumn>()));
public void LoadGrid()
{
if (gridColumns.Count > 0)
myGrid.Columns.Clear();
foreach (DataGridColumn c in gridColumns)
{
myGrid.Columns.Add(c);
}
}
아동 Xaml :
<local:parentControl x:Name="deGrid">
<local:parentControl.gridColumns>
<toolkit:DataGridTextColumn Width="Auto" Header="1" Binding="{Binding Path=.}" />
<toolkit:DataGridTextColumn Width="Auto" Header="2" Binding="{Binding Path=.}" />
</local:parentControl.gridColumns>
</local:parentControl>
And finally, the tricky part is finding where to call 'LoadGrid'.
I am struggling with this but got things to work by calling after InitalizeComponent
in my window constructor (childGrid is x:name in window.xaml):
childGrid.deGrid.LoadGrid();
You might be able to do this with AutoGenerateColumns and a DataTemplate. I'm not positive if it would work without a lot of work, you would have to play around with it. Honestly if you have a working solution already I wouldn't make the change just yet unless there's a big reason. The DataGrid control is getting very good but it still needs some work (and I have a lot of learning left to do) to be able to do dynamic tasks like this easily.
There is a sample of the way I do programmatically:
public partial class UserControlWithComboBoxColumnDataGrid : UserControl
{
private Dictionary<int, string> _Dictionary;
private ObservableCollection<MyItem> _MyItems;
public UserControlWithComboBoxColumnDataGrid() {
_Dictionary = new Dictionary<int, string>();
_Dictionary.Add(1,"A");
_Dictionary.Add(2,"B");
_MyItems = new ObservableCollection<MyItem>();
dataGridMyItems.AutoGeneratingColumn += DataGridMyItems_AutoGeneratingColumn;
dataGridMyItems.ItemsSource = _MyItems;
}
private void DataGridMyItems_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var desc = e.PropertyDescriptor as PropertyDescriptor;
var att = desc.Attributes[typeof(ColumnNameAttribute)] as ColumnNameAttribute;
if (att != null)
{
if (att.Name == "My Combobox Item") {
var comboBoxColumn = new DataGridComboBoxColumn {
DisplayMemberPath = "Value",
SelectedValuePath = "Key",
ItemsSource = _ApprovalTypes,
SelectedValueBinding = new Binding( "Bazinga"),
};
e.Column = comboBoxColumn;
}
}
}
}
public class MyItem {
public string Name{get;set;}
[ColumnName("My Combobox Item")]
public int Bazinga {get;set;}
}
public class ColumnNameAttribute : Attribute
{
public string Name { get; set; }
public ColumnNameAttribute(string name) { Name = name; }
}
'Programing' 카테고리의 다른 글
Amazon 제품 API-기본 개요 및 정보 찾기 (0) | 2020.07.16 |
---|---|
const_iterator와 iterator의 차이점은 무엇입니까? (0) | 2020.07.16 |
node.js process.memoryUsage ()의 반환 값은 무엇을 의미합니까? (0) | 2020.07.16 |
gmake와 make의 차이점은 무엇입니까? (0) | 2020.07.16 |
JRE 또는 JDK가 설치되어 있는지 확인하는 방법 (0) | 2020.07.16 |