microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.31k stars 676 forks source link

Proposal: UIElement/FrameworkElement should expose protected method to add/initialize VisualTree #5530

Open michael-hawker opened 3 years ago

michael-hawker commented 3 years ago

Proposal: UIElement/FrameworkElement should expose protected method to add/initialize VisualTree

Follow-up from discussion on docs repo here: https://github.com/MicrosoftDocs/winrt-api/issues/646

Summary

Currently, especially in C#, it's not possible to inherit directly from FrameworkElement for a low-level component. Even in WinUI 2 with C++ here, while they look like they're inheriting from FrameworkElement, they're really using Panel (see DeriveFromPanelHelper_base) as an intermediary even if only a single child is expected.

The ability to initialize a Child or Children property into the Visual Tree that's being created on a subclass of FrameworkElement is not exposed even as a protected member within the framework. This means that while you can write code that inherits directly from FrameworkElement, there's no way to get it to render as it can't be added to the VisualTree itself.

Rationale

In the Windows Community Toolkit, we wanted to add a low-level component akin to Viewbox for constraining to a specific aspect ratio. It made sense to make it a single-child control from FrameworkElement, but we can't do that directly as we can't add the Child property we create to the Visual Tree. See https://github.com/CommunityToolkit/WindowsCommunityToolkit/pull/4104.

Instead we need to inherit from Panel if we want something simple, but then that allows for multiple children to be added (which is odd for this scenario) OR we need to inherit from ContentPresenter which does more than we need it to do with templating support.

Scope

Capability Priority
This proposal will allow developers to accomplish initialize and render a low-level UI component Must
This proposal will allow end users to accomplish react to remove and replace content when a developer changes the content of the sub-classed control Must
This proposal will allow developers to create an element which accepts multiple children Could

API Proposal

Example of what a proposed API could look like with a protected AddChild and RemoveChild method for UIElement:

  public MyClass : FrameworkElement
  {
      propdp UIElement Child; // Shorthand

      private static void ChildPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {      
          if (d is FrameworkElement element && e.OldValue != e.NewValue)
          {
             element.RemoveChild(e.OldValue); // new protected method

             if (e.NewValue != null)
             {
                 element.AddChild(e.NewValue); // new protected method
             }
          }
      }
 }

Open Questions

StephenLPeters commented 3 years ago

@MikeHillberg and @chrisglein FYI

michael-hawker commented 2 years ago

Hit this working on my polyfill for Adorners as well as AdornerDecorator really just wants to be a Decorator (i.e. FrameworkElement in UWP/WinUI) and then set two children instead transparently even though in XML it has the single Child content property.

michael-hawker commented 1 year ago

Bumping, though think this is a feature request, based on https://github.com/microsoft/microsoft-ui-xaml/discussions/8638