[英]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希望CompanyItems
是GridItem
的屬性,事實並非如此,這就是綁定失敗的原因。
我已經嘗試像這樣使用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集合? 有可能嗎?
預先感謝您的幫助!
請檢查以下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上有關DataGridComboBoxColumn
的ItemsSource
的文檔說,只有靜態資源,靜態代碼或組合框項目的內聯集合可以綁定到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.