WPF DatagridのComboBoxColumnのバインディングアイテムソース
-
29-10-2019 - |
質問
2つのシンプルなモデルクラスとビューモデルがあります...
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はMainWindowに設定されています DataContext
app.xaml.cs:
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
のデータグリッドの GridItems
ViewModelのコレクション。このパートは機能し、「ジム」という名前の単一グリッドラインが表示されます。
私も設定したいです ItemsSource
すべての行のコンボボックスの CompanyItems
ViewModelのコレクション。この部分は機能しません。コンボボックスは空のままで、デバッガー出力ウィンドウにエラーメッセージが表示されます。
System.Windows.Dataエラー:2:ターゲット要素の統治フレームワークセレメントまたは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 Error:4:参照「RecativeSource Findancestor、ancestortype = 'system.windows.window'、ancestorlevel = '1' '。 BindingExpression:path = companyItems; dataitem = null;ターゲット要素は「datagridcomboboxcolumn」(hashcode = 1150788)です。ターゲットプロパティは「itemsSource」(タイプ「IENUMERABLE」)です
質問:DatagridComboBoxColumnのアイテムソースをViewModelの会社のコレクションにどのようにバインドできますか?まったく可能ですか?
よろしくお願いします!
解決
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
:
ドロップダウンリストを入力するには、最初に次のオプションのいずれかを使用して、コンボボックスのItemsSourceプロパティを設定します。
- 静的リソース。詳細については、stateCresourceマークアップ拡張機能を参照してください。
- X:静的コードエンティティ。詳細については、X:静的マークアップ拡張機能を参照してください。
- コンボボクシテムタイプのインラインコレクション。
DataContextのプロパティへのバインドは、それを正しく理解していれば不可能です。
そして確かに:私が作るとき CompanyItems
a 静的 ViewModelのプロパティ...
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}" />
...その後、それは機能します。しかし、静的プロパティとしてアイテムソースを持つことは時々大丈夫かもしれませんが、それは必ずしも私が望むものではありません。
正しい解決策は次のとおりです。
<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歳以上であることに気付きましたが、同様の問題に対処することでつまずき、将来の旅行者を助けるかもしれない場合に別の潜在的な解決策を共有すると思いました。私の机の上の最も近いオブジェクトの叫びと投げの間のスタックフローの上をフロップします)。
私の場合、DatagridComboBoxColumnの代わりにDatagridTemplateColumnを使用して、次のスニペットを使用することで、必要な効果を得ることができました。 [警告:.NET 4.0を使用していますが、私が読んでいることは、Datagridが多くの進化を行ったと信じるように導きます。
<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; }
}
コンボボックスはバインドしようとしています GridItem[x].CompanyItems
, 、存在しません。
あなたの相対的なバインディングは近いですが、それはにバインドする必要があります DataContext.CompanyItems
window.companyitemsが存在しないためです
私が使用するbastの方法私はテキストブロックとコンボボックスを同じプロパティにバインドすると、このプロパティはnotifypropertychangedをサポートする必要があります。
relativeresourceを使用して、usercontextである親ビューデータアコントロールでバインドしてバインディングで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>