rdel
rdel

Reputation: 21

WPF DataGrid ContentPresenter Binding Errors

I'm using a WPF Datagrid with DataGridTemplateColumns with comboboxes in each cell. At startup, the output window repeats the following message and delays startup about 10 seconds. Something related to ContentPresenter and DataContext not being set (DataItem=null). Please help if you can. Here is the error message:

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:(no path); DataItem=null; target element is 'ContentPresenter' (Name=''); target property is 'Content' (type 'Object')

It's not technically an error, but it delays the startup nonetheless. Here is a subset of the xaml:

<DataGrid x:Name="grid"  
          AutoGenerateColumns="False" 
          CanUserAddRows="True" 
          IsEnabled="True" Grid.Row="1" Grid.Column="0" 
          EnableRowVirtualization="False"
          HorizontalAlignment="Left"
          VerticalAlignment="Center"
          ScrollViewer.CanContentScroll="True"
          GridLinesVisibility="Vertical" 
          AreRowDetailsFrozen="True"
          HorizontalScrollBarVisibility="Visible"
          VerticalScrollBarVisibility="Visible"
          SelectionMode="Extended"
          HeadersVisibility="All"
          Height="750"
          VirtualizingStackPanel.VirtualizationMode="Standard"
          VirtualizingStackPanel.IsVirtualizing="True"
          DataContext="{StaticResource vm}"
          ItemsSource="{Binding Source={StaticResource vm}, Path=CorpActionAutoPostConfigs, Mode=TwoWay, IsAsync=False}">

         <DataGrid.Columns>
            <!-- selecteditembinding: source:enum, dest:JournalType -->
            <DataGridTemplateColumn  Header="JournalType" x:Name="colJournalType">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox x:Name="cbJournalTypes"
                                    ItemsSource="{Binding Source={StaticResource vm}, Path=JournalTypes, IsAsync=False}" 
                                    ItemTemplate="{StaticResource GenericDataTemplate}"
                                    SelectedItem="{Binding Path=JournalTypeCode, Mode=TwoWay, Converter={StaticResource JournalTypeConverter}, IsAsync=False}">
                            <ComboBox.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <VirtualizingStackPanel />
                                </ItemsPanelTemplate>
                            </ComboBox.ItemsPanel>
                        </ComboBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

...more similar columns follow. I feel like I need to set a Style or ControlTemplate or something but not exactly sure how to proceed.

If I use a ListView/GridView structure, these "errors" do not occur and startup is much faster. But I would prefer to use the DataGrid.

One clue is it seems I get that error for each visible cell that is generated. So I tried to define a style for DataGridCell, that sets the control template for each cell and includes a ContentPresenter binding with a fallback value. Did not resolve the errors.

        <Style TargetType="{x:Type DataGridCell}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="DataGridCell" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
                        <Border BorderThickness="{TemplateBinding Border.BorderThickness}" 
                        BorderBrush="{TemplateBinding Border.BorderBrush}" 
                        Background="{TemplateBinding Panel.Background}" 
                        SnapsToDevicePixels="True">
                        <ContentPresenter x:Name="DataGridCellContentPresenter"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, FallbackValue=null}"
                        ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
                        ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" 
                        SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

EDIT:

Looking at the Visual Tree, it seems the ContentPresenter I have defined in the ControlTemplate contains yet another ContentPresenter. That ContentPresenter is nameless and I suspect is the source of the binding errors. The parent of that ContentPresenter is the Border. Does anyone know how to define this ContentPresenter in a ControlTemplate so I can add a fallback value?

I can't yet add a screencap of the visual tree, but here is what it looks like:

Upvotes: 2

Views: 1377

Answers (1)

AwkwardCoder
AwkwardCoder

Reputation: 25631

A little late to the party, but I managed a work around for this issue - if derive your own custom class from DataGridTemplateColumn you can explicitly set the FallbackValue for the Binding.

Obviously you then have to go through the XAML and replace the required elements.

    public sealed class DataGridTemplateColumnEx : DataGridTemplateColumn
    {
        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            return LoadTemplateContent(false, dataItem, cell);
        }

        protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
        {
            return LoadTemplateContent(true, dataItem, cell);
        }

        private void ChooseCellTemplateAndSelector(bool isEditing, out DataTemplate template, out DataTemplateSelector templateSelector)
        {
            template = null;
            templateSelector = null;

            if (isEditing)
            {
                template = CellEditingTemplate;
                templateSelector = CellEditingTemplateSelector;
            }

            if (template == null && templateSelector == null)
            {
                template = CellTemplate;
                templateSelector = CellTemplateSelector;
            }
        }

        private FrameworkElement LoadTemplateContent(bool isEditing, object dataItem, DataGridCell cell)
        {
            ChooseCellTemplateAndSelector(isEditing, out var template, out var templateSelector);
            if (template != null || templateSelector != null)
            {
                var contentPresenter = new ContentPresenter();

                var binding = new Binding
                {
                    // Explicitly setting this to NULL, stops the binding FallbackValue messages, and therefore improves perf...
                    FallbackValue = null
                };

                BindingOperations.SetBinding(contentPresenter, ContentPresenter.ContentProperty, binding);
                contentPresenter.ContentTemplate = template;
                contentPresenter.ContentTemplateSelector = templateSelector;
                return contentPresenter;
            }

            return null;
        }
    }

Upvotes: 0

Related Questions