wpf icon indicating copy to clipboard operation
wpf copied to clipboard

DynamicResource is not working for Header property in DataGridTextColumn

Open vijayarasan opened this issue 3 years ago • 7 comments

  • .NET Core Version: .Net 6.0
  • Does the bug reproduce also in WPF for .NET Framework 4.8?: Yes

Problem description:

I have defined the DynamicResource for Header property in DataGridTextColumn of DataGrid. Please refer the below code snippet,

<Application.Resources>
        <System:String x:Key="firstName"  >Before Change Name</System:String>
        <System:String x:Key="secondName" >After Change Name</System:String>
    </Application.Resources>
<DataGridTextColumn Binding="{Binding CustomerID}" 
                                    Header="{DynamicResource firstName}" />

When change the resource at runtime Header property does not change the value in DataGrid. Can you please check and elaborate why DynamicResource is not working in DataGridTextColumn.Header property of DataGrid?

Note : If we use HeaderTemplate property in DataGridTextColumn its working properly in DataGrid. Please refer the code snippet,

<Window.Resources>
        <DataTemplate x:Key="headerTemplate">
            <TextBlock Height="50"                    
                    Text="{DynamicResource firstName}"
                    TextWrapping="Wrap" />
        </DataTemplate>
</Window.Resources>   

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="200" />
        </Grid.ColumnDefinitions>
        <DataGrid x:Name="dataGrid"                                 
                  ItemsSource="{Binding Orders}"
                  AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding OrderID}" 
                                    HeaderTemplate="{StaticResource headerTemplate}" />
                <DataGridTextColumn Binding="{Binding CustomerID}" 
                                    Header="{DynamicResource firstName}" />
                <DataGridTextColumn Binding="{Binding CustomerName}" Header="Customer Name" />
                <DataGridTextColumn Binding="{Binding Country}" Header="Country" />
                <DataGridTextColumn Binding="{Binding UnitPrice}" Header="Unit Price" />
            </DataGrid.Columns>
        </DataGrid>
</Grid>

Please refer the Screenshot for your reference Resource

Minimal repro: Step 1: Run the sample

Step 2: Click the Change resource button

Actual behavior: DynamicResource is not working for Header property in DataGridTextColumn

Expected behavior: DynamicResource is working properly for Header property in DataGridTextColumn

I have attached the sample for your reference. Sample Link: DataGrid.zip

Can you please check and provide the solution to use the DynamicResource in Header property in DataGridTextColumn?

Regards, Vijayarasan S

vijayarasan avatar Mar 23 '22 12:03 vijayarasan

The DataGridColumns are only descriptors, based on which the actual elements are created. The column headers are presented by DataGridColumnHeadersPresenter, an ItemsControl using DataGridColumnHeader as a container. When DataGridColumnHeader container is created, the property values are transferred onto it from DataGridColumn, e.g. the DataGridColumn.Header property onto DataGridColumnHeader.Content property (similar with templates):

https://github.com/dotnet/wpf/blob/b63c69eaf5b58e758765a37bb18064bbc94832ad/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Primitives/DataGridRowHeader.cs#L326-L331

Notably, it's property values, not the DP expressions that are copied over.


Now the DynamicResource resp. ResourceReferenceExpression needs something that can provide the resources. It looks for an inheritance context of the target DependencyObject. However, DataGridColumn not only is not in the visual tree as discussed above, it inherits directly from DependencyObject and does not implement any inheritance context.

There is a special provision in the ResourceReferenceExpression for the cases where the target object does not have any inheritance context, which fallbacks to app and/or system resources. In the example above, this is where the initial value comes from:

https://github.com/dotnet/wpf/blob/89d172db0b7a192de720c6cfba5e28a1e7d46123/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceReferenceExpression.cs#L150-L151


Finally, when resource is changed, the owner of the ResourceDictionary is notified. For Framework[Content]Element owners, the ResourcesChanged event is raised. For app resources, the event is propagated through the tree in app's windows:

https://github.com/dotnet/wpf/blob/89d172db0b7a192de720c6cfba5e28a1e7d46123/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceDictionary.cs#L1690

If ResourcesReferenceExpression can get hold of inheritance context in the form of Framework[Content]Element, it can simply subscribe to the ResourcesChanged event and propagate the changes to the target DO. Any changes to the app resources will come to it through the visual tree.

As we noted above, in this scenario we have neither inheritance context nor visual tree. The app/system resources have no other means of notifying of changes, so the ResourceReferenceExpression does not have any way to subscribe to the changes in this case.


I believe that an easy way to hotfix this issue would be to for the DataGridColumn to return DataGridOwner as its InheritanceContext. (That sounds like a reasonable idea anyway, unless I missed some undesirable consequences.)

However, that would not fix the issue for other types in this situation (including user types). It seems that Application and perhaps even SystemResources should have an event that the ResourceReferenceExpression could subscribe to to learn about changes to these resources.

miloush avatar Apr 02 '22 23:04 miloush

@vijayarasan

For this one you are mixing two things together, you have to pick either of the ones mentioned below.

Either use

<Window.Resources>
        <DataTemplate x:Key="headerTemplate">
            <TextBlock Height="50"                    
                    Text="{DynamicResource firstName}"
                    TextWrapping="Wrap" />
        </DataTemplate>
    </Window.Resources>  

and

<DataGridTextColumn Binding="{Binding OrderID}" 
                                    HeaderTemplate="{StaticResource headerTemplate}" />

Or use

<Window.Resources>
        <TextBlock x:Key="TextBlockName" Height="50"                    
                    Text="{DynamicResource firstName}"
                    TextWrapping="Wrap" />
    </Window.Resources> 

and

<DataGridTextColumn Binding="{Binding OrderID}" 
                                    Header="{StaticResource TextBlockName}" />

In this first approach we are setting style with the help of header template, and in the second one we are setting it with the help of static resource.

I hope this addresses your question, in such a case we can close it.

anjaligupta-dev avatar Apr 03 '22 17:04 anjaligupta-dev

@anjalisheel-wpf this does not address the fact that the DynamicResource extension does not work on DataGridColumn.

miloush avatar Apr 03 '22 17:04 miloush

Hi @anjalisheel-wpf,

Sorry for the delay,

The provided workaround does not satisfy our requirements. Our need is to achieve this requirement in a simple way like the below,


HeaderText={DynamicResource something}.

vijayarasan avatar Oct 07 '22 14:10 vijayarasan

Hi @miloush,

Sorry for the delay.

Seems the mentioned PR is still in progress. When we expect the fix for the reported issue

vijayarasan avatar Oct 07 '22 15:10 vijayarasan

Why is "simple way" a requirement? I am not doing any more work on the PR, I was looking for more feedback on whether that is a reasonable way to support this scenario.

miloush avatar Oct 07 '22 15:10 miloush

Hi @miloush,

The purpose of requesting the simple way to access the property that is bound for HeaderText is a very simple comparatively process with HeaderTemplate

vijayarasan avatar Oct 18 '22 14:10 vijayarasan

I know this issue was last updated long ago but I ran over this problem today. This is the simpliest way to solve this I found.

<DataGrid.Columns>
    <DataGridTextColumn>
        <DataGridTextColumn.Header>
            <TextBlock Text="{DynamicResource gridColumnHeader1}" />
        </DataGridTextColumn.Header>
    </DataGridTextColumn>
</DataGrid.Columns>

p-gillet avatar Mar 19 '25 14:03 p-gillet