[.net] 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은 DataContextApp.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();
    }
}

보시다시피 ItemsSourceDataGrid를 GridItemsViewModel 컬렉션으로 설정 했습니다. 이 부분이 작동하면 이름이 “Jim”인 단일 그리드 선이 표시됩니다.

또한 ItemsSource모든 행의 ComboBox를 CompanyItemsViewModel 컬렉션 으로 설정하고 싶습니다 . 이 부분은 작동하지 않습니다. ComboBox가 비어 있고 디버거 출력 창에 오류 메시지가 표시됩니다.

System.Windows.Data 오류 : 2 : 대상 요소에 대한 관리 FrameworkElement 또는 FrameworkContentElement를 찾을 수 없습니다. BindingExpression : Path = CompanyItems; DataItem = null; 대상 요소는 ‘DataGridComboBoxColumn'(HashCode = 28633162)입니다. 대상 속성은 ‘ItemsSource'( ‘IEnumerable’유형)입니다.

WPF 는 그렇지 않은 CompanyItems속성이 될 것으로 예상 GridItem하고 바인딩이 실패하는 이유입니다.

나는 이미 a RelativeSourceAncestorType같은 작업을 시도했습니다 .

<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이 제공됩니다.

또한, 퍼팅 CompanyItem로부터 직접 액세스 할 목록은 GridItem당신이 없애 수 있습니다RelativeSource .

IMHO, 이것은 당신에게 매우 깨끗한 솔루션을 제공합니다.

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>

모델보기 :

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; }
}


답변

ComboBox가 GridItem[x].CompanyItems존재하지 않는 에 바인딩하려고 합니다.

RelativeBinding이 닫혀 있지만 DataContext.CompanyItemsWindow.CompanyItems가 존재하지 않기 때문에 바인딩해야 합니다.


답변

내가 사용하는 인색 한 방법은 textblock과 combobox를 동일한 속성에 바인딩 하고이 속성은 notifyPropertyChanged를 지원해야합니다.

이 경우 datagrid는 datagrid.itemsource에서 사용한 객체에서 데이터 그리드가 검색되기 때문에 바인딩에서 datagrid 수준으로 올라가는 usercontrol 인 parent view datacontext에 바인딩하는 relativeresource를 사용했습니다.

<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>