Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gallery only shows items once, then shows blank? #910

Closed
DaveInCaz opened this issue Mar 3, 2021 · 11 comments
Closed

Gallery only shows items once, then shows blank? #910

DaveInCaz opened this issue Mar 3, 2021 · 11 comments

Comments

@DaveInCaz
Copy link

Summary - my galleries are only showing contents a single time. Hopefully someone here has seen this before... so I'll try to describe the problem briefly but can add more details or code if that's helpful.


I have a Fluent:DropDownButton which has several items in its contents including two Fluent:Gallery objects.

Each gallery has its ItemSource bound to an ObservableCollection<Thing> and each has an ItemTemplate / DataTemplate for Thing.

This is the strange behavior I'm trying to debug:

  • Run application & open window containing the ribbon
  • Collections are initialized (they don't change again in this scenario)
  • User clicks the dropdown
  • Galleries are shown - look fine
  • Click away from the dropdown
  • Click the dropdown a second time
  • Galleries are empty (??)

In the debugger I can see that the collections have not changed at all. Gallery.HasItems = false, and yet Gallery.ItemsSource.Count > 0 at the same time.

So I'm fairly stumped at this... I've never seen any other ItemsControl have this behavior.

It very well may not be an issue with Fluent:Ribbon at all, but I thought I should start here. Thank you!


Environment

  • Fluent.Ribbon: Originally I was using version 7.1.0 but updating to 8.0.3 didn't make any difference.
  • Windows 10
  • .NET Framework 4.7.2
@batzen
Copy link
Member

batzen commented Mar 3, 2021

That sounds really strange.
Will try to reproduce this and ask for a repro if I can't.

@batzen
Copy link
Member

batzen commented Mar 3, 2021

The showcase application already contains a test case similar to your use case and it's working fine there.
Could you create and attach a repro for your issue?

@DaveInCaz
Copy link
Author

DaveInCaz commented Mar 3, 2021

@batzen thank you very much for looking into this, I really appreciate your time & input. I've dug deeper and found out some more details.

Because of memory leaks caused by binding to plain CLR objects (non-DOs, and without INotifyPropertyChanged) we registered a class handler for the Unloaded event which does this:

BindingOperations.ClearAllBindings(object_being_unloaded);

If I take this code out then the gallery never loses its contents, so this is definitely the cause. (And maybe its not a great fix). But it only seems to break the gallery...

If I substitute a ListBox in place of the Fluent.Gallery (and leave everything else alone as much as possible) then the problem does not occur. I used ListBox for this comparison since Gallery derives from it.

Watching the binding diagnostics I see lines like this when the gallery unloads:

System.Windows.Data Warning: 79 : BindingExpression (hash=38193748): Deactivate
System.Windows.Data Warning: 103 : BindingExpression (hash=38193748): Replace item at level 0 with {NullDataItem}
System.Windows.Data Warning: 103 : BindingExpression (hash=38193748): Replace item at level 1 with {NullDataItem}
System.Windows.Data Warning: 63 : BindingExpression (hash=38193748): Detach

I haven't seen messages like this for other controls.

So I guess the question boils down to why the Gallery ends up with its bindings lost after it is unloaded, while other controls do not? Does it do something special or unusual when it unloads?

@batzen
Copy link
Member

batzen commented Mar 6, 2021

Having a class handler clearing all bindings on unload is a very bad idea.
Unloaded does not mean destroyed.
Controls can be loaded again and that seems to be what happens in your case.

Instead of clearing all bindings it would be way better, and cleaner, to just implement INotifyPropertyChanged in all classes you are binding to. You just have to implement the interface, you don't have to send any change notifications.

Gallery has no code for unloaded events, so i don't know what's causing your issues.
To further investigate that i would need a repro from you.

@DaveInCaz
Copy link
Author

DaveInCaz commented Mar 17, 2021

@batzen I've attached a solution which just reproduces the difference I noticed between ListBox and Fluent:Gallery.

Gallery_X_ListBox.zip

Steps to reproduce:

  1. Build the solution & run
  2. Click the button on Window 1. Window 2 will appear.
  3. Click the first dropdown in the ribbon. Items are shown.
  4. Click it again. No items are shown.
  5. Click the second dropdown in the ribbon. Items are shown.
  6. Click it again. Items are still shown.
  7. Close Window 2
  8. Click the button again - the entire cycle will repeat from Implement Touch Mode #4
  9. Close the program

Variation 2:

  1. In Window2.xaml.cs, uncomment line 19 (so it now excludes Gallery from having bindings cleared)
  2. Build & run
  3. Repeat the steps above. Items will always be shown from the Gallery dropdown.

I agree that clearing bindings is far from ideal. It was done as a workaround for another problem (*), but interestingly this was the only control (that I am aware of) which exhibited any change in behavior at all. So maybe its worth understanding why that is.

(*) External classes without property change notification. Probably need to wrap them in new classes instead.

@batzen
Copy link
Member

batzen commented Mar 17, 2021

Just had a look at your repro.
The first thing i noticed is that the ListBox does not even get unloaded while the gallery does get unloaded.
So it's not a different behavior according to clearing all bindings but to the unloading.
Will try to find out why the gallery gets unloaded, but the listbox does not.

@batzen
Copy link
Member

batzen commented Mar 17, 2021

Ok, it's a bug in WPF itself.
If you add Loaded="FrameworkElement_OnLoaded" to the ListBox the behavior is the same.

This might also be interesting for your memory leak workaround.
If you don't have a loaded handler your unloaded class event handler won't be called.

@DaveInCaz
Copy link
Author

DaveInCaz commented Mar 18, 2021

Interesting conclusion...!

Do I understand correctly the following implications:

  • The unloaded routed event is only being raised for controls which have a non-null loaded event? (Or, at least, only being raised in some circumstances). And this is the WPF bug?
  • Internally, Fluent Gallery probably sets a loaded event handler

@batzen
Copy link
Member

batzen commented Mar 18, 2021

Not every control needs the loaded handler.
It depends on the tree and who in that tree has a loaded handler.

In this case no one in the tree for the ListBox has a loaded handler, so the unloaded class event is not fired for anyone when closing the popup.

The gallery on the other hand has a loaded handler, so the unloaded class event is fired for every control that's more up in the tree. So you get events for the gallery, some borders, some decorators and the popup.

The above would also be true if someone directly handled the unloaded event and it does only not work when using a class event handler.

The "offending" line is https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/BroadcastEventHelper.cs,332 and the same in line https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/BroadcastEventHelper.cs,393

I am not sure if everyone would calls this a bug in WPF.
I would as it's more than surprising and very unexpected. I wasn't aware of this restriction for class event handlers.

@DaveInCaz
Copy link
Author

Thanks yet again for your clear explanation & help / time looking into this.

Do you think it is worth reporting this on https://github.com/dotnet/wpf/issues ? At the very least it seems like this behavior should be documented (if they don't consider it a bug).

For my purposes this issue is closed now, as obviously it is not caused by Fluent Ribbon at all.

Best regards,
Dave

@batzen
Copy link
Member

batzen commented Mar 18, 2021

You should report it there and mention me and this issue there.
This behavior should at least be documented.
Currently the documentation does not list any special cases and only says "Class handling is a feature that is available for routed events. [...]".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants