WPF DataGrid에서 ComboBoxColumn의 ItemsSource 바인딩
두 개의 간단한 Model 클래스와 ViewModel이 있습니다.
public class GridItem
{
public string Name { get; set; }
public int CompanyID { get; set; }
}
public class CompanyItem
{
public int ID { get; set; }
public string Name { get; set; }
}
public class ViewModel
{
public ViewModel()
{
GridItems = new ObservableCollection<GridItem>() {
new GridItem() { Name = "Jim", CompanyID = 1 } };
CompanyItems = new ObservableCollection<CompanyItem>() {
new CompanyItem() { ID = 1, Name = "Company 1" },
new CompanyItem() { ID = 2, Name = "Company 2" } };
}
public ObservableCollection<GridItem> GridItems { get; set; }
public ObservableCollection<CompanyItem> CompanyItems { get; set; }
}
... 그리고 간단한 창 :
<Window x:Class="DataGridComboBoxColumnApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding GridItems}" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" />
<DataGridComboBoxColumn ItemsSource="{Binding CompanyItems}"
DisplayMemberPath="Name"
SelectedValuePath="ID"
SelectedValueBinding="{Binding CompanyID}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
ViewModel은 DataContext
App.xaml.cs 의 MainWindow로 설정됩니다 .
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
ViewModel viewModel = new ViewModel();
window.DataContext = viewModel;
window.Show();
}
}
보시다시피 ItemsSource
DataGrid를 GridItems
ViewModel 컬렉션으로 설정 했습니다. 이 부분이 작동하면 이름이 "Jim"인 단일 그리드 선이 표시됩니다.
또한 ItemsSource
모든 행의 ComboBox를 CompanyItems
ViewModel 컬렉션 으로 설정하고 싶습니다 . 이 부분은 작동하지 않습니다. ComboBox가 비어 있고 디버거 출력 창에 오류 메시지가 표시됩니다.
System.Windows.Data 오류 : 2 : 대상 요소에 대한 관리 FrameworkElement 또는 FrameworkContentElement를 찾을 수 없습니다. BindingExpression : Path = CompanyItems; DataItem = null; 대상 요소는 'DataGridComboBoxColumn'(HashCode = 28633162)입니다. 대상 속성은 'ItemsSource'( 'IEnumerable'유형)입니다.
WPF 는 그렇지 않은 CompanyItems
속성이 될 것으로 예상 GridItem
하고 바인딩이 실패하는 이유입니다.
나는 이미 a RelativeSource
와 AncestorType
같은 작업을 시도했습니다 .
<DataGridComboBoxColumn ItemsSource="{Binding CompanyItems,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Window}}}"
DisplayMemberPath="Name"
SelectedValuePath="ID"
SelectedValueBinding="{Binding CompanyID}" />
하지만 디버거 출력에 또 다른 오류가 발생합니다.
System.Windows.Data 오류 : 4 : 'RelativeSource FindAncestor, AncestorType ='System.Windows.Window ', AncestorLevel ='1 ''참조가있는 바인딩에 대한 소스를 찾을 수 없습니다. BindingExpression : Path = CompanyItems; DataItem = null; 대상 요소는 'DataGridComboBoxColumn'(HashCode = 1150788)입니다. 대상 속성은 'ItemsSource'( 'IEnumerable'유형)입니다.
질문 : DataGridComboBoxColumn의 ItemsSource를 ViewModel의 CompanyItems 컬렉션에 바인딩하려면 어떻게해야합니까? 전혀 가능합니까?
미리 도와 주셔서 감사합니다!
Pls, 아래 DataGridComboBoxColumn xaml이 작동하는지 확인하십시오.
<DataGridComboBoxColumn
SelectedValueBinding="{Binding CompanyID}"
DisplayMemberPath="Name"
SelectedValuePath="ID">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.CompanyItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.CompanyItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
여기에서 직면 한 문제에 대한 또 다른 해결책을 찾을 수 있습니다 . WPF DataGrid와 함께 콤보 상자 사용
[정보 MSDN에 대한 문서 ItemsSource
의는DataGridComboBoxColumn
정적 자원, 콤보 상자 항목의 정적 코드 또는 인라인 컬렉션이 결합 될 수 있다고 말한다 ItemsSource
:
드롭 다운 목록을 채우려면 먼저 다음 옵션 중 하나를 사용하여 ComboBox의 ItemsSource 속성을 설정합니다.
- 정적 자원. 자세한 내용은 StaticResource 마크 업 확장을 참조하세요.
- x : Static 코드 엔티티. 자세한 내용은 x : Static Markup Extension을 참조하십시오.
- ComboBoxItem 유형의 인라인 컬렉션입니다.
올바르게 이해하면 DataContext의 속성에 바인딩 할 수 없습니다.
그리고 실제로 : 나는 할 때 정적의 뷰 모델에 속성을 ...CompanyItems
public static ObservableCollection<CompanyItem> CompanyItems { get; set; }
... ViewModel이있는 네임 스페이스를 창에 추가합니다.
xmlns:vm="clr-namespace:DataGridComboBoxColumnApp"
... 바인딩을 ...로 변경하십시오.
<DataGridComboBoxColumn
ItemsSource="{Binding Source={x:Static vm:ViewModel.CompanyItems}}"
DisplayMemberPath="Name"
SelectedValuePath="ID"
SelectedValueBinding="{Binding CompanyID}" />
... 그러면 작동합니다. 그러나 ItemsSource를 정적 속성으로 사용하는 것은 때때로 괜찮을 수 있지만 항상 원하는 것은 아닙니다.
올바른 해결책은 다음과 같습니다.
<Window.Resources>
<CollectionViewSource x:Key="ItemsCVS" Source="{Binding MyItems}" />
</Window.Resources>
<!-- ... -->
<DataGrid ItemsSource="{Binding MyRecords}">
<DataGridComboBoxColumn Header="Column With Predefined Values"
ItemsSource="{Binding Source={StaticResource ItemsCVS}}"
SelectedValueBinding="{Binding MyItemId}"
SelectedValuePath="Id"
DisplayMemberPath="StatusCode" />
</DataGrid>
위의 레이아웃은 나에게 완벽하게 작동하며 다른 사람들에게도 작동합니다. 이 디자인 선택도 의미가 있지만 어디에도 잘 설명되어 있지는 않습니다. 그러나 미리 정의 된 값이있는 데이터 열이있는 경우 해당 값은 일반적으로 런타임 중에 변경되지 않습니다. 따라서 CollectionViewSource
데이터를 만들고 초기화하는 것이 합리적입니다. 또한 조상을 찾고 데이터 컨텍스트에 바인딩하기 위해 더 긴 바인딩을 제거합니다 (항상 나에게 잘못되었다고 느꼈습니다).
이 바인딩으로 어려움을 겪은 다른 사람을 위해 여기에 남겨두고 더 나은 방법이 있는지 궁금해했습니다 (이 페이지가 여전히 검색 결과에 나타나기 때문에 여기에 왔습니다).
나는이 질문이 1 년이 넘었다는 것을 알고 있지만 비슷한 문제를 처리하는 과정에서 우연히 발견했고 그것이 미래의 여행자 (또는 나 자신이 나중에 이것을 잊고 나를 발견 할 때 도움이 될 수있는 경우에 대비하여 다른 잠재적 인 해결책을 공유 할 것이라고 생각했습니다) 내 책상에서 가장 가까운 물체의 비명과 던지기 사이에 StackOverflow에서 펄럭입니다.
제 경우에는 DataGridComboBoxColumn 대신 DataGridTemplateColumn을 사용하여 원하는 효과를 얻을 수있었습니다. [주의 : 저는 .NET 4.0을 사용하고 있으며, 제가 읽은 내용은 DataGrid가 많은 발전을했다고 믿게 만들었습니다. 따라서 이전 버전을 사용한다면 YMMV]
<DataGridTemplateColumn Header="Identifier_TEMPLATED">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox IsEditable="False"
Text="{Binding ComponentIdentifier,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=ApplicableIdentifiers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ComponentIdentifier}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
RookieRick이 맞습니다. DataGridTemplateColumn
대신 DataGridComboBoxColumn
사용하면 훨씬 더 간단한 XAML이 제공됩니다.
Moreover, putting the CompanyItem
list directly accessible from the GridItem
allows you to get rid of the RelativeSource
.
IMHO, this give you a very clean solution.
XAML:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding GridItems}" >
<DataGrid.Resources>
<DataTemplate x:Key="CompanyDisplayTemplate" DataType="vm:GridItem">
<TextBlock Text="{Binding Company}" />
</DataTemplate>
<DataTemplate x:Key="CompanyEditingTemplate" DataType="vm:GridItem">
<ComboBox SelectedItem="{Binding Company}" ItemsSource="{Binding CompanyList}" />
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" />
<DataGridTemplateColumn CellTemplate="{StaticResource CompanyDisplayTemplate}"
CellEditingTemplate="{StaticResource CompanyEditingTemplate}" />
</DataGrid.Columns>
</DataGrid>
View model:
public class GridItem
{
public string Name { get; set; }
public CompanyItem Company { get; set; }
public IEnumerable<CompanyItem> CompanyList { get; set; }
}
public class CompanyItem
{
public int ID { get; set; }
public string Name { get; set; }
public override string ToString() { return Name; }
}
public class ViewModel
{
readonly ObservableCollection<CompanyItem> companies;
public ViewModel()
{
companies = new ObservableCollection<CompanyItem>{
new CompanyItem { ID = 1, Name = "Company 1" },
new CompanyItem { ID = 2, Name = "Company 2" }
};
GridItems = new ObservableCollection<GridItem> {
new GridItem { Name = "Jim", Company = companies[0], CompanyList = companies}
};
}
public ObservableCollection<GridItem> GridItems { get; set; }
}
Your ComboBox is trying to bind to bind to GridItem[x].CompanyItems
, which doesn't exist.
Your RelativeBinding is close, however it needs to bind to DataContext.CompanyItems
because Window.CompanyItems does not exist
the bast way i use i bind the textblock and combobox to same property and this property should support notifyPropertyChanged.
i used relativeresource to bind to parent view datacontext which is usercontrol to go up datagrid level in binding because in this case the datagrid will search in object that you used in datagrid.itemsource
<DataGridTemplateColumn Header="your_columnName">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SelectedUnit.Name, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox DisplayMemberPath="Name"
IsEditable="True"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.UnitLookupCollection}"
SelectedItem="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SelectedUnit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding UnitId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Id" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
참고URL : https://stackoverflow.com/questions/5409259/binding-itemssource-of-a-comboboxcolumn-in-wpf-datagrid
'Program Tip' 카테고리의 다른 글
IntelliJ 새 프로젝트-Maven Archetype 목록이 비어 있음 (0) | 2020.10.25 |
---|---|
내 스크립트에서 사용할 사용자 지정 형식을 PowerShell에서 만들려면 어떻게해야합니까? (0) | 2020.10.25 |
Java에서 부울 배열 채우기 (0) | 2020.10.25 |
Hudson과 CruiseControl for Java 프로젝트의 차이점은 무엇입니까? (0) | 2020.10.25 |
C #을 사용한 연도의 날짜 차이 (0) | 2020.10.25 |