簡體   English   中英

在WPF DataGrid中綁定ComboBoxColumn的ItemsSource

[英]Binding ItemsSource of a ComboBoxColumn in WPF DataGrid

我有兩個簡單的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>

在App.xaml.cs中,ViewModel設置為MainWindow的DataContext

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

如您所見,我將DataGrid的ItemsSource設置為ViewModel的GridItems集合。 此部分有效,顯示名稱為“ Jim”的單個網格線。

我還想將ComboBox的ItemsSource設置為ViewModel的CompanyItems集合。 這部分不起作用:ComboBox保持為空,並且在Debugger Output窗口中,我看到一條錯誤消息:

System.Windows.Data錯誤:2:找不到目標元素的管理FrameworkElement或FrameworkContentElement。 BindingExpression:Path = CompanyItems; DataItem = null; 目標元素是“ DataGridComboBoxColumn”(HashCode = 28633162); 目標屬性為“ ItemsSource”(類型為“ IEnumerable”)

我相信WPF希望CompanyItemsGridItem的屬性,事實並非如此,這就是綁定失敗的原因。

我已經嘗試像這樣使用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集合? 有可能嗎?

預先感謝您的幫助!

請檢查以下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上有關DataGridComboBoxColumnItemsSource文檔說,只有靜態資源,靜態代碼或組合框項目的內聯集合可以綁定到ItemsSource

若要填充下拉列表,請首先使用以下選項之一設置ComboBox的ItemsSource屬性:

  • 靜態資源。 有關更多信息,請參見StaticResource標記擴展。
  • x:靜態代碼實體。 有關更多信息,請參見x:靜態標記擴展。
  • ComboBoxItem類型的嵌入式集合。

如果我正確理解,則不可能綁定到DataContext的屬性。

的確如此:當我在ViewModel中將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並初始化數據一次是有意義的。 它還擺脫了較長的綁定,以找到祖先並綁定到其數據上下文(這對我來說總是很不對勁)。

我將其留給所有為此綁定工作苦苦掙扎的人,並想知道是否有更好的方法(因為此頁面顯然仍出現在搜索結果中,這就是我到達這里的方式)。

我意識到這個問題已有一年多的歷史了,但是我偶然發現了一個類似的問題,並認為我將分享另一個潛在的解決方案,以防它可能對未來的旅行者(或我自己)有所幫助(當我以后忘記這個並找到自己的時候)在我的桌子上尖叫和拋出最近的物體之間在StackOverflow上翻轉)。

就我而言,我可以通過使用DataGridTemplateColumn而不是DataGridComboBoxColumn來獲得所需的效果,以下代碼段為例。 [注意:我正在使用.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

恕我直言,這為您提供了一個非常干凈的解決方案。

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 ,該GridItem[x].CompanyItems不存在。

您的RelativeBinding已關閉,但是它需要綁定到DataContext.CompanyItems因為Window.CompanyItems不存在

我使用的最猛烈的方式是將文本塊和組合框綁定到相同的屬性,並且此屬性應支持notifyPropertyChanged。

我使用relativeresource綁定到父視圖datacontext,該上下文是用戶控制的,以便在綁定中上升到datagrid級別,因為在這種情況下,datagrid將在您在datagrid中使用的對象中搜索。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM