MaterialDesignInXamlToolkit icon indicating copy to clipboard operation
MaterialDesignInXamlToolkit copied to clipboard

Broken binding errors after clicking on DataGrid header

Open TysonMN opened this issue 4 years ago • 22 comments

Under a particular set of circumstances involving Material Design in XAML, clicking the header of a DataGrid causes WPF to log nine warnings to PresentationTraceSources.DataBindingSource.

As far as I can tell, each of the following is necessary (i.e. this is a minimal working reproduction).

  1. A ResourceDictionary containing the lines for colors and themes of Material Design in XAML as given in the Super Quick Start.
  2. A XAML file that defines the DataGrid and also adds that ResourceDictionary to the Resources of its top-level FrameworkElement. ~3. The entry point of the application is a static Main method.~ ~4. That entry point instantiates the singleton Application and sets its Resources to those in the ResourceDictionary mentioned above.~
  3. Application sets its Resources to those in the ResourceDictionary mentioned above.

Code is available in this repo or in this ZIP.

Steps to reproduce

  1. Run the application in Visual Studio
  2. Open the Output window
  3. Click the header of the DataGrid

Expected behavior

Nothing appears in the Output window

Actual behavior

WPF logs nine binding errors to PresentationTraceSources.DataBindingSource, which show up in the Output window.

2020-12-19_15-47-15_435

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGridRow', AncestorLevel='1''. BindingExpression:Path=Foreground; DataItem=null; target element is 'DataGridCell' (Name=''); target property is 'Foreground' (type 'Brush')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=HorizontalGridLinesBrush; DataItem=null; target element is 'DataGridRowHeader' (Name=''); target property is 'BorderBrush' (type 'Brush')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=GridLinesVisibility; DataItem=null; target element is 'DataGridRowHeader' (Name=''); target property is 'BorderThickness' (type 'Thickness')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGridRow', AncestorLevel='1''. BindingExpression:Path=Foreground; DataItem=null; target element is 'DataGridCell' (Name=''); target property is 'Foreground' (type 'Brush')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=HorizontalGridLinesBrush; DataItem=null; target element is 'DataGridRowHeader' (Name=''); target property is 'BorderBrush' (type 'Brush')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=GridLinesVisibility; DataItem=null; target element is 'DataGridRowHeader' (Name=''); target property is 'BorderThickness' (type 'Thickness')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGridRow', AncestorLevel='1''. BindingExpression:Path=Foreground; DataItem=null; target element is 'DataGridCell' (Name=''); target property is 'Foreground' (type 'Brush')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=HorizontalGridLinesBrush; DataItem=null; target element is 'DataGridRowHeader' (Name=''); target property is 'BorderBrush' (type 'Brush')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=GridLinesVisibility; DataItem=null; target element is 'DataGridRowHeader' (Name=''); target property is 'BorderThickness' (type 'Thickness')

TysonMN avatar Dec 19 '20 21:12 TysonMN

Edited to add: This comment is not longer relevant since I was able to simply my reproduction.

The entry-point of my application is a static Main method (instead of through Applicaiton.StartupUri) because I use Elmish.WPF.

My entry point instantiates the singleton Application and sets its Resources to those in that ResourceDictionary as a workaround to issue #2040 (which has been fixed but not yet appeared in a release).

Consider the following change to the code. This is still a workaround to issue #2040 and achieves the expected behavior described above in the provided example code. Unfortunately, that same change in my application at work still exhibits the actual behavior described above. This means my "minimal working reproduction" isn't quite so minimal (or maybe it is too minimal).

-- app.Resources = new DefaultResourceDicitionaryContainer().Resources; + app.Resources = mainWindow.Resources;

TysonMN avatar Dec 19 '20 22:12 TysonMN

The three paths among those errors are Foreground, HorizontalGridLinesBrush, and GridLinesVisibility. The last two are rare enough that I can list all references. All fives references to HorizontalGridLinesBrush and four references to GridLinesVisibility occur in these four code snippets.

https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/blob/c36307b01f5a47f0b9cf1ede8adb4d7ca5546a34/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.DataGrid.xaml#L187-L189

https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/blob/c36307b01f5a47f0b9cf1ede8adb4d7ca5546a34/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.DataGrid.xaml#L256-L262

https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/blob/c36307b01f5a47f0b9cf1ede8adb4d7ca5546a34/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.DataGrid.xaml#L337-L339

https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/blob/c36307b01f5a47f0b9cf1ede8adb4d7ca5546a34/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.DataGrid.xaml#L424-L439

TysonMN avatar Dec 21 '20 21:12 TysonMN

Same, my application crashes when I click on the header. I have this issue when columns are autogenerated. If columns are not autogenerated, I don't have any problem

ElieTaillard avatar Dec 22 '20 13:12 ElieTaillard

@Xaalek, can you share link to a repo or ZIP that reproduces the behavior you describe?

My application doesn't crash, so your issue might be a different problem (but I am still interested to see your reproduction).

TysonMN avatar Dec 22 '20 13:12 TysonMN

System.Windows.Documents.Run is not a Visual or Visual 3D object image

System.InvalidOperationException HResult=0x80131509 Message='System.Windows.Documents.Run' n'est pas un objet Visual ou Visual3D. Source=PresentationCore StackTrace: at MS.Internal.Media.VisualTreeUtils.AsVisual(DependencyObject element, Visual& visual, Visual3D& visual3D) at MS.Internal.Media.VisualTreeUtils.AsNonNullVisual(DependencyObject element, Visual& visual, Visual3D& visual3D) at System.Windows.Media.VisualTreeHelper.GetParent(DependencyObject reference) at MaterialDesignThemes.Wpf.Extensions.<GetVisualAncestry>d__3.MoveNext() at System.Linq.Enumerable.<OfTypeIterator>d__951.MoveNext() at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable1 source) at MaterialDesignThemes.Wpf.DataGridAssist.AllowDirectEditWithoutFocus(Object sender, MouseButtonEventArgs mouseArgs) at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent) at System.Windows.UIElement.OnPreviewMouseDownThunk(Object sender, MouseButtonEventArgs e) at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget) at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target) at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs) at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised) at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args) at System.Windows.ContentElement.RaiseTrustedEvent(RoutedEventArgs args) at System.Windows.Input.InputManager.ProcessStagingArea() at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input) at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport) at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel) at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) at System.Windows.Application.RunDispatcher(Object ignore) at System.Windows.Application.RunInternal(Window window) at System.Windows.Application.Run(Window window) at TransitRoute.Application.Main()

This exception was originally thrown at this call stack: [External Code]

ElieTaillard avatar Dec 22 '20 13:12 ElieTaillard

I don't know why but when I change the header of the columns it fixes the issue.

@TysonMN This is my XAML code <DataGrid Name="DataGridRech" FrozenColumnCount ="4" AutoGenerateColumns="True" IsReadOnly="True" ClipboardCopyMode="ExcludeHeader" AlternatingRowBackground="#FFEDEBE9"> </DataGrid>

This is my code behind

    Dim cmd As New SqlCommand()
    cmd.CommandText = "SELECT * FROM temp_rech_dossier_route"
    cmd.Connection = connSQLServer
    Dim da As New SqlDataAdapter(cmd)
    Dim dt As New DataTable("RECH")
    da.Fill(dt)
    DataGridRech.ItemsSource = dt.DefaultView

ElieTaillard avatar Dec 22 '20 14:12 ElieTaillard

When executing the EXE in the bin directory, neither my MWE (linked above) or my application at work crash when clicking on the header of the DataGrid.

@Xaalek, if you provide executable code that reproduces the behavior you describe, then I will look into your problem. I don't know how to test only using the code snippets that you provided.

However, for both my MWE (after adding a popup on a binding error) and in my application at work, these binding errors do not occur when executing the EXE in the bin directory. I am starting to think this is an issue with Visual Studio. I will submit a bug report to them.

TysonMN avatar Dec 22 '20 15:12 TysonMN

@TysonMN My code is in VB .Net and I'm using a DataBase to populate my datagrid. So it would be complicated to test it.

But here is my code in C#

SqlCommand cmd = new SqlCommand(); cmd.CommandText = "SELECT * FROM temp_rech_dossier_route"; cmd.Connection = connSQLServer; SqlDataAdapter da = new SqlDataAdapter(cmd); DataTable dt = new DataTable("RECH"); da.Fill(dt); DataGridRech.ItemsSource = dt.DefaultView;

If you want to test it, you will have to put it in a event for exemple window load event. Moreover you will have to replace the connSQLServer by your sql connection, replace DataGridRech by the name of your datagrid and finally change the sql query.

ElieTaillard avatar Dec 22 '20 16:12 ElieTaillard

I am not sure I could correctly do all that...but if you are willing to provide a minimal working example, then I will test it.

TysonMN avatar Dec 22 '20 16:12 TysonMN

I created this issue on Visual Studio Developer Community about the different behavior when running through Visual Studio and vs directly running the EXE.

TysonMN avatar Dec 22 '20 18:12 TysonMN

These are the five styles with DataGrid in their name. https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/blob/c36307b01f5a47f0b9cf1ede8adb4d7ca5546a34/MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Defaults.xaml#L58-L62

If I comment out the first four and just leave DataGridRowHeader, then only the six binding errors for HorizontalGridLinesBrush and GridLinesVisibility occur.

TysonMN avatar Dec 22 '20 20:12 TysonMN

My entry point instantiates the singleton Application and sets its Resources to those in that ResourceDictionary as a workaround to issue #2040 (which has been fixed but not yet appeared in a release).

Consider the following change to the code. This is still a workaround to issue #2040 and achieves the expected behavior described above in the provided example code. Unfortunately, that same change in my application at work still exhibits the actual behavior described above. This means my "minimal working reproduction" isn't quite so minimal (or maybe it is too minimal).

--- app.Resources = new DefaultResourceDicitionaryContainer().Resources; ++ app.Resources = mainWindow.Resources;

I figured out a workaround for my case. I just realized that Material Design in XAML publishes a (pre-release) NuGet package each time a pull request is completed. I have updated to the latest pre-release, which includes a fix for #2040, and then used the quoted workaround (which, in my case, is to simply not set the Resources of Application).

I will no longer be actively investigating this issue, but I will follow up on any suggestions.

TysonMN avatar Dec 22 '20 20:12 TysonMN

System.Windows.Documents.Run is not a Visual or Visual 3D object image

System.InvalidOperationException HResult=0x80131509 Message='System.Windows.Documents.Run' n'est pas un objet Visual ou Visual3D. Source=PresentationCore StackTrace: at MS.Internal.Media.VisualTreeUtils.AsVisual(DependencyObject element, Visual& visual, Visual3D& visual3D) at MS.Internal.Media.VisualTreeUtils.AsNonNullVisual(DependencyObject element, Visual& visual, Visual3D& visual3D) at System.Windows.Media.VisualTreeHelper.GetParent(DependencyObject reference) at MaterialDesignThemes.Wpf.Extensions.d__3.MoveNext() at System.Linq.Enumerable.d__951.MoveNext() at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable1 source) at MaterialDesignThemes.Wpf.DataGridAssist.AllowDirectEditWithoutFocus(Object sender, MouseButtonEventArgs mouseArgs) ... at TransitRoute.Application.Main()

This exception was originally thrown at this call stack: [External Code]

I reported steps to reproduce this issue here #2134, with possible solution but, unfortunately, I had no time to work on it. You can grab it if you want.

LoRdPMN avatar Jan 04 '21 17:01 LoRdPMN

Both this issue and #2134 include clicking on the header of a DataGrid as a reproduction step. Then they continue separately by describing different issues that result. Do you think they have more in common? For example, do you think they might have the same fix?

TysonMN avatar Jan 04 '21 18:01 TysonMN

I don't think so... I have a lot of complex DataGrids, including ones with custom headers and none of them are causing binding warnings. Did you tried my workaround on #2134? I saw that you are using the 3.2.0 version, can you try to reproduce using the lastest pre-release version?

LoRdPMN avatar Jan 04 '21 19:01 LoRdPMN

I figured out the cause of your issue on your repo 😃 You are merging "MyDictionary.xaml" both into your App.xaml and Window.xaml. Merging it into App.xaml is enough for the whole application, so if you just remove

  <Window.Resources>
    <ResourceDictionary Source="MyDictionary.xaml"/>
  </Window.Resources>

it works as expected!

LoRdPMN avatar Jan 04 '21 19:01 LoRdPMN

I saw that you are using the 3.2.0 version, can you try to reproduce using the latest pre-release version?

Testing the latest prerelease is a good idea. I didn't think of that. I just tested version 4.0.0-ci2347, but the bad behavior remains.

Did you tried my workaround on #2134?

I just did, but the bad behavior remains.

I figured out the cause of your issue on your repo 😃 You are merging "MyDictionary.xaml" both into your App.xaml and Window.xaml. Merging it into App.xaml is enough for the whole application, so if you just remove

  <Window.Resources>
    <ResourceDictionary Source="MyDictionary.xaml"/>
  </Window.Resources>

it works as expected!

Sure. As I stated in https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/issues/2196#issuecomment-749757926, I was able to avoid this bug by not including MyDictionary.xaml in App.xaml and including MyDictionary.xaml in MainWindow.xaml.

But what do you mean by "the cause"? I agree that including MyDictionary.xaml in both App.xaml and MainWindow.xaml is necessary to reproduce the bad behavior. In this content, I think of a "cause" as an explanation for the behavior. Do you know why in principle bad behavior would result from including the same ResourceDictionary in both App.xaml and some Window? I don't know of any reason for this.

TysonMN avatar Jan 04 '21 20:01 TysonMN

I can't tell exactly without digging deep into the library code, but probably it sets some animations bindings with relative source on the fly, so doing it twice probably is breaking it. Also it is best to not create duplicate resource allocations by merging it again.

Sure. As I stated in #2196 (comment), I was able to avoid this bug by not including MyDictionary.xaml in App.xaml and including MyDictionary.xaml in MainWindow.xaml.

Sorry I didn't see that, but anyways, I think the best is to merge it on App.xaml instead of each view, unless you need views without material design.

LoRdPMN avatar Jan 05 '21 11:01 LoRdPMN

Sorry I didn't see that

No problem.

I can't tell exactly without digging deep into the library code, but probably it sets some animations bindings with relative source on the fly, so doing it twice probably is breaking it.

Interesting. There is an arrow that rotates 180 degrees (to point up or down) when clicking on the header. That is a lead for someone to investigate at some point.

it is best to not create duplicate resource allocations by merging it again. ... I think the best is to merge it on App.xaml instead of each view, unless you need views without material design.

I don't typically have an App.xaml file since I use Elmish.WPF. See, for example, the SimpleCounter sample.

Maybe an Elmish.WPF application can also have a traditional App.xaml file. I will investigate this.

TysonMN avatar Jan 05 '21 20:01 TysonMN

I am having the same problem but only when I collapse one of my columns. It is initially collapsed. When I set it visible there are no problems. Only when I disable it I get two binding errors which is reproducable every time I collapse it again.

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=HorizontalGridLinesBrush; DataItem=null; target element is 'DataGridColumnHeader' (Name=''); target property is 'BorderBrush' (type 'Brush')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGrid', AncestorLevel='1''. BindingExpression:Path=GridLinesVisibility; DataItem=null; target element is 'DataGridColumnHeader' (Name=''); target property is 'BorderThickness' (type 'Thickness')
                <DataGridTextColumn 
                    Header="Properties" 
                    Binding="{Binding Properties}" 
                    Visibility="{Binding Data.DisplayProperties,Source={StaticResource Proxy},Converter={StaticResource BooleanToVisibilityConverter}}"/>

It also happens if I change the Visibility of the column via code behind.

ThomasMader avatar Jan 13 '21 15:01 ThomasMader

@ThomasMader, can share a MWE that exhibits that behavior?

TysonMN avatar Jan 13 '21 15:01 TysonMN

Here you go. [1] I took your example and reworked it to reproduce my problem. The problem appears only when MaterialDesignDataGridCell is used as CellStyle and MaterialDesignDataGridColumnHeader as ColumnHeaderStyle.

[1] https://drive.google.com/file/d/1Hig3byFZltPY4gJKXiFZZ3tahFCNS3px/view?usp=sharing

ThomasMader avatar Jan 16 '21 12:01 ThomasMader