WPF DataGrid에서 ComboBoxColumn의 ItemsSource 바인딩


82

두 개의 간단한 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 컬렉션에 바인딩하려면 어떻게해야합니까? 전혀 가능합니까?

미리 도와 주셔서 감사합니다!

답변:


123

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와 함께 콤보 상자 사용


4
지옥, 이것은 작동합니다 !!! 이유 만 이해할 수 있다면? 그리고 Rachel이 권장하는 변경 사항이 적용된 원래 코드는 어떻습니까? 어쨌든 대단히 감사합니다!
Slauma 2011 년

1
여기에서 설명을 찾을 수 있다고 생각합니다 : wpf.codeplex.com/workitem/8153?ProjectName=wpf (댓글 참조)
serge_gubenko 2011 년

1
그들은이 버그 ( "우리는 향후 릴리스에서 수정 될 내부 데이터베이스에 버그를 제출했습니다.")를 기능으로 바꾸기로 결정한 것 같습니다. 이 스레드에서 내 대답을 살펴보십시오.이 문제는 문서로 해결되었으며 이것이 결코 변경되지 않을 것이라는 강력한 표시입니다.
Slauma 2011 년

1
+1 joemorrison.org/blog/2009/02/17/… 링크. 내 문제를 해결했습니다. 짜증나, ~ 5 시간 동안 우리가하고 있던 다른 작업을 위해 이미이 유형이 내 프로젝트에 있다는 것을 깨달았습니다. (항상 학습 과정입니다.
TravisWhidden 2011 년

나를 위해 작동하지 않습니다. EditingElementStyle이 작동하는 것처럼 보이지만 ElementStyle을 추가하면 어떤 이유로 든 (DisplayMemberPath의 값 대신) 아무것도 채우지 않는 ComboBox가 생기고 클릭 할 때 EditingElementStyle로 다시 전환되지 않습니다.
William

46

그만큼 [정보 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를 정적 속성으로 사용하는 것은 때때로 괜찮을 수 있지만 항상 원하는 것은 아닙니다.


1
나는 아직도 마이크로 소프트가이 버그를 고칠 것을 희망한다
juFo 2014 년

38

올바른 해결책은 다음과 같습니다.

<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
틀림없이 좋은 대답이지만 아마도 OP의 질문에서 추상화 되었을 것 입니다. 귀하는 MyItems영업 이익의 코드와 함께 사용할 경우 컴파일 오류로 이어질 것
MickyD

22

나는이 질문이 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>

처음 몇 가지 답변으로 어려움을 겪은 후 이것을 시도했고 나에게도 효과적이었습니다. 감사.
coson

7

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

4

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

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


답변 감사합니다! 나는 그것을 시도했지만 ( 내 질문의 마지막 XAML 코드 조각에서 대체 CompanyItemsDataContext.CompanyItems) 디버거 출력에서 ​​동일한 오류가 발생합니다.
Slauma 2011 년

1
@Slauma 나는 확실하지 않다. XAML에서 볼 수있는 유일한 점은 Mode = FindAncestor이며 일반적으로 생략합니다. 루트 창에 이름을 지정하고 RelativeSource를 사용하는 대신 바인딩에서 이름으로 참조하려고 했습니까? {Binding ElementName=RootWindow, Path=DataContext.CompanyItems}
Rachel

두 가지를 모두 시도했지만 (Mode = FindAncestor를 생략하고 이름이 지정된 요소로 바인딩을 변경했습니다) 작동하지 않습니다. 이 방법이 당신에게 효과가 있다는 것이 이상합니다. 이 간단한 테스트 응용 프로그램을 만들어 내 응용 프로그램에서 문제를 매우 간단한 컨텍스트로 끌어 왔습니다. 내가 무엇을 잘못 만들 수 있는지 모르겠습니다. 질문에 표시된 코드는 전체 응용 프로그램 (VS2010의 WPF 프로젝트 템플릿에서 생성됨)입니다.이 코드에는 더 이상 아무것도 없습니다.
Slauma 2011 년

1

내가 사용하는 인색 한 방법은 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>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.