Custom control with cascading DataContext for children in the collection

I am trying to create a user control with similar functionality, such as a DataGrid (but a DataGrid is not suitable here).

What I would like to achieve looks something like this:

<my:CustomList ItemsSource="{Binding Items}"> <my:CustomList.Columns> <my:Column Width="60" Binding="{Binding MyCustomProperty}" /> </my:CustomList.Columns> </my:CustomList> 

where Elements will come from ViewModel (for example) as follows:

 public ObservableCollection<Item> Items { get; set; } public class Item { public string MyCustomProperty { get; set; } public string MyAnotherCustomProperty { get; set; } } 

I have a problem with binding to MyCustomProperty.

If I inherit my user control from a DataGrid and use its columns, the DataContext streams from ItemsSource to Bindings in the columns are just fine. I would like to do the same with my custom control that does not inherit from DataGrid. What is the magic of DataGrid.Columns data getting context from ItemsSource?

Edit: Let me ask about it differently:

If I implement a custom DataGridColumn

 public class MyDataGridColumn : DataGridBoundColumn { private Binding _bindingSubText; public Binding BindingSubText { get { return _bindingSubText; } set { if (_bindingSubText == value) return; var oldBinding = _bindingSubText; _bindingSubText = value; OnBindingChanged(oldBinding, _bindingSubText); } } protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) { var textTextBlock = new TextBlock(); var bindingText = Binding ?? new Binding(); textTextBlock.SetBinding(TextBlock.TextProperty, bindingText); var textSubTextBlock = new TextBlock(); var bindingSubText = BindingSubText ?? new Binding(); textSubTextBlock.SetBinding(TextBlock.TextProperty, bindingSubText); var stackPanel = new StackPanel() { Orientation = Orientation.Vertical }; stackPanel.Children.Add(textTextBlock); stackPanel.Children.Add(textSubTextBlock); return stackPanel; } protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) { // I don't want to edit elements return null; } } 

and try using it in XAML as follows:

 <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False"> <DataGrid.Columns> <my:MyDataGridColumn Binding="{Binding MyCustomProperty}" BindingSubText="{Binding MyAnotherCustomProperty}" /> </DataGrid.Columns> </DataGrid> 

The binding for the BindingSubText property should still come with the DataContext of the DataGrid parent, offering me the elements. MyAnotherCustomProperty will have wigglies in the designer, but it will work in standby mode (due to dynamic binding). My problem is that someone else will use this custom DataGridColumn, he / she will need to know this and will have the β€œwrong” IntelliSense to bind.

How is the context for the Binding DataGridColumn property set, so that IntelliSense works as expected?

+7
c # wpf custom-controls datacontext
source share
2 answers

Your question is very broad, but first of all, if you want to extend the DataGrid, consider redefining its style and adding triggers or something like that.

This is a question in your design .. and it’s hard to say that it is right or wrong.

The column property in the DataGrid and GridView is more or less just a dummy object containing values ​​that later will need cells, for example, element templates, snapping, width, height ... etc. Suppose it looks like these columns do not have true DataContext ...

The DataContext is passed along the functional visual / logical tree, but there are no column properties. That is why you have your problems. All the bindings you can set in such a column are actually place owners. However, the cells take part in the visual tree, because they are their own controls, and so when the cell is generated, they do things like this before they are drawn: cellTextBlock.SetBinding (TextBlock.TextProperty, columnBindingPlaceHolder), cellTextBlock.SetBinding ( TextBlock. HeightProperty, columnHeightPlaceHolder).

Cells use those column placeholders

If you want your own column property to have the same DataContext as the DataGrid, consider changing ObseravableCollection to FreezableCollection. Also make the Column object a Freezable object. Just work with Freezables. Because in WPF, they have the ability to inherit a DataContext. For example, brushes in Wpf are freezables.

Hope this helps you further.

+2
source share

I think one of the ancestors of the DataGrid handles this (perhaps ItemsControl). If your control is not derived from the ItemsControl, you need to handle it manually (when adding a new column to your control, explicitly specify its context).

Now I see that your control is also derived from ItemsControl. Then I propose to process it manually.

-one
source share

All Articles