Microsoft-ui-xaml: Proposal: AdaptiveTrigger for Control Sizing

Created on 9 May 2019  路  12Comments  路  Source: microsoft/microsoft-ui-xaml

Proposal: AdaptiveTrigger for Control Sizing

Summary

AdaptiveTrigger is an incredibly powerful component of XAML. However, individual controls sometimes need adaptive layout that is not dependent on the window size, but rather on the size they are given to layout. It would be great to see a way to define this adaptive behavior in XAML instead of being forced to wire up on SizeChanged.

Rationale

This benefits control authors, both externally creating reusable components, and developers creating their own. By simplifying the sizing markup into XAML instead of code-behind, we can leverage better code reuse and clarity.

feature proposal needs-winui-3 team-Controls

Most helpful comment

I'd love to see AdaptiveTrigger be able to monitor the size of its host instead of the full window too. Also, it should support building responsive apps from desktop to mobile, not only the other way around. @Code-ScottLe should have more input on this.

All 12 comments

@adamhewitt627 - Thank you for the great suggestion. Is there a specific control or scenario you've run into where this functionality would be particularly useful?

@adamhewitt627: I agree completely. There are many scenarios where children are sized by their parent, such as proportional '*' sizing of Grid Rows/Columns that contain stretched child Controls, something that may be independent of Window dimensions. An AdaptiveTrigger (or some new trigger) should be able to update a Control's VisualState to the state for the appropriate dimensions based on Control size. There are already custom triggers available that do so, but we should at least consider bringing some of these trigger types in to the platform. https://github.com/Herdo/AdaptiveTriggerLibrary

@YuliKl A great example is something like using TwoPaneView, especially when paired with resizable panes. You can even see this behavior in the Windows Mail app as you resize the messages list. The window size is unchanged, but the item templates adjust layout dynamically.

I'd love to see AdaptiveTrigger be able to monitor the size of its host instead of the full window too. Also, it should support building responsive apps from desktop to mobile, not only the other way around. @Code-ScottLe should have more input on this.

Warning, long post ahead, grab a cup of coffee or something :)

This is a scenario that I have been struggling with for awhile now. At the current state, AdaptiveTrigger relies on the single event that fired up on the entire window being resized, this is problematic in 2 common scenarios.

  • With the introduction with "page containers" like the NavigationView, the actual "workable" space for the page within the container is almost always less than the width and height of the window due to the container own layout. This create major challenge for the developer that now they have to measure the precise space that they don't have (in the case of NavigationView, it will also depends on which mode they are in - top nav, side hamburger collapsed/extended) so AdaptiveTrigger can be fired at the right time. Also, most of the time, container like this does not expose their overlay element with and height anyway, and people have to guess most of the time.

  • Another problematic scenario , is for developer who write custom control / data templates. Generally, custom controls are meant to be placed in-line with others controls but often are complex enough that they have to be written separately. While DataTemplate is something that people have to write very often when dealing with ListView and their own data. It is extremely hard for people to write adaptive UX for these elements and have them resize properly without doing their own plumbing. More critically, these custom controls / ListView does not take up the entire page itself most of the time, making it even harder to know when to trigger the UX resizing with AdaptiveTrigger.

On the other size note, like @JustinXinLiu pointed out, AdaptiveTrigger is currently one-way development only. You scale up (from smalls screen to big screen) with AdaptiveTrigger but there isn't a way to do it backward. AdaptiveTrigger works by using MinWindowHeight and MinWindowWidth to specify the correct size to fire the trigger. This severely force developer to start with small screen layout and expand it larger because there is no way for the developer to start with big screen and then trickle it down.

  • This is very counter-intuitive if the audience of the application is very likely to be on the big screen, and the developer now just want to support smaller screen without breaking everyone else.

  • This also purpose the problem of the developer having to re-write the existing UX (which probably is optimized for big screen already). They will have to re-write their existing big-screen optimized layout entirely in the Setters region of AdaptiveTrigger after re-writing the smaller screen XAML + write plumbing code so they can adjust the UI with the setter.

Consider the following example:

<!-- Existing XAML , complex layout with optimization for big screen here, let say 1366x768-->
<!-- Very common scenario with desktop-centric WPF app -->

<!-- Now if we want to enable support for screen that is having width less than 768px, how do we do that? --?
<VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="AdaptiveVisualStateGroup">
                <VisualState x:Name="Compact">
                    <VisualState.StateTriggers>
                            <!-- Because AdaptiveTrigger is one-way scale up only, we have to start with Min Width = 0, which will ALWAYS be triggered/stay true because the window width will never <= 0 -->
                            <AdaptiveTrigger MinWindowWidth = 0 />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                           <!-- If you write setters here, it will always be executed the moment the page is navigated to -->
                    </VisualState.Setters>
                </VisualState>

                <VisualState x:Name="DefaultWide">
                     <VisualState.StateTriggers>
                            <!-- And because we added an AdaptiveTrigger which is always gonna get triggered the moment the page is navigated to, we need to add this here so we can resize it back ? -->
                            <AdaptiveTrigger MinWindowWidth = 768 />
                    </VisualState.StateTriggers>

                    <VisualState.Setters>
                           <!-- If you write setters for the state above, you will have to undo all of them here. -->
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

We can already see how complex it is for the developer to migtigate their current UX to the more modern design pattern, but there is also another hurdle to jump through. When the page is navigated to on a big screen (larger than 1366x768), the original XAML layout will be parsed and created (in this case, the big screen layout). THEN, the AdaptiveTrigger with the MinWindowWidth = 0 will be activated/triggered (because 768px > 0), causing the setters to run and set the layout to compact mode. AND THEN, the AdaptiveTrigger with the MinWindowWith = 768 will also be activated/triggered, causing the setters to also run and set the layout back to "DefaultWide". In many case, this is a race condition, and it is not clear to know what the UX will end up in which mode. I have had many caseses that the page was stuck in Compact mode because it was executed after the DefaultWide.

We are living in the time of transitioning between desktop workload and mobile workload. I will agree that starting off with mobile first sometime works, but it won't fit in the current landscape of people trying to migtigate their hardened and combat proven desktop app in a more modern design workflow that we have with UWP. I think this is a very sound problem that needs to be addressed. I do have a code snippet, a custom Trigger that i wrote myself (probably not very good and error prone, based on the StateTriggerBase), but i can share that as a start point if this gain any traction.

Warning: opinions ahead
That it's hard to start with something large and adjust it when there's less space shouldn't be an issue as, conceptually, it's a bad way to approach a design problem.

Taking a large amount of content, information, or number of controls and trying to squeeze them into smaller amount of space almost always leads to a sub-par solution.

It's much better to take a step back and approach the challenge from the perspective of "What's the minimum we need to have of the screen?" and then expanding and adding more as space increases.
Yes, this may mean adopting a different Information Architecture or changing the screens that make up an app and how a person navigates between them.

If a company/developer wants to transition an app from being desktop based (which I assume to mean based on a fairly large screen size and with minimal consideration for changes in screen real estate) to a more modern/flexible design, this is the perfect time to educate on why this is the best time to reconsider the structure and layout of an apps UI.
Investing in making it easier for people to try and squeeze an old design into a new style/environment/design language is not the best use of anyone's time.
Teach and provide tooling to help make a good app, not push people to what's likely to be a bad one.

There are also performance benefits for starting with a design that considers the minimum case first as it means only loading into the UI what's actually needed.

The same principles apply to individual controls as apps/screens. Start small and work your way up, adding more when possible/appropriate. Don't try and squeeze large amounts into small spaces or add things to the UI that you then need to remove or hide.

If people are taking existing WinForms or WPF apps and expecting the screen design to work the same way when they convert them to a modern/flexible approach, this sounds like an opportunity to educate rather than a need to change the way UWP works and ignore the many years of experience gained in bringing desktop apps to "mobile".
Remember kids: "Mobilize, don't miniaturize."

All this being said, I am supportive of the original proposal though. Having controls that can easily adjust to their available space will be a valuable addition.

If a company/developer wants to transition an app from being desktop based (which I assume to mean based on a fairly large screen size and with minimal consideration for changes in screen real estate) to a more modern/flexible design, this is the perfect time to educate on why this is the best time to reconsider the structure and layout of an apps UI.

As much as I want people to just redesign the UI from scratch to leverage all of the new features and do it the right way, but the reality is a bit more harsh, especially in the enterprise world. In my experience, they won't invest into this without seeing what could be possible in the fastest way possible. In other word, the overhead of seeing something that can be (not will be) a solution going forward is too much. That is why i stated the "hardened/combat proven desktop app" for this kind of problem. It is already working, why re-investing into a new stack with some massive (for them) overhead without a clear idea if things work.

In this case, the audience is the desktop developer. They are already familiar with the wide screen development. I think we should enable them a path to go down stream rather than forcing them to throw away their existing knowledge and work it backward.

There are also performance benefits for starting with a design that considers the minimum case first as it means only loading into the UI what's actually needed.

I think you are making certain assumption here. If the application is mainly used on the big desktop, why having an overhead of setting the UI to compact version, just so to expand it out to wide mode? In many cases, if the application is expected to be mainly used on a desktop, this will be a perf hit.

Remember kids: "Mobilize, don't miniaturize."

Also imho, all design doesn't have to be started with small and compact. If you have space and be more comfortable with it, why can't they start from the middle? Developing something tablet-centric with the ability to go big on the screen and also scale down to phone should be possible without having to committed to either and have to compromise the main audience in the progress. Each application has a core audience, the app developer will have to focus on them, but we don't want to block the possibility of them adventuring on smaller size either. Just my 2 cents.

P/S: we are all discussing here, i have been giving this a lot of thoughts in the last few years and finally a chance to express some of those :)

Whatever the best philosophy is for creating responsive UI - the proposal if accepted, should work in both directions. Whether it is big to small, or small to big

The issue is, the current AdaptiveTrigger implementation doesn't work if you go from big to small...

And I personally always design for big first because IMHO the bigger the screen is, the harder it is to design, and I want to get the harder one done first then the other sizes would get a lot easier. Not to mention most UWP apps we build are desktop apps.

I understand everyone's got their own preference but at least it should be a choice for the devs.

Media queries based on element sizes have been discussed and ditched from the CSS responsive standard because they're selectors that depend on layout, and so they're cyclical.

I don't know if in XAML there is the same problem, i link this issue only for reference (maybe XAML is better and can do it!).

@dpierangeli That is a good point, and I nearly discussed something to that effect in my proposal, but couldn't quite state it clearly, and so I left it for further conversation. @Code-ScottLe even briefly touched on the challenge of triggering the adaptive state "at the right time."

However, there _are_ times when an element's size just fills the parent, and layout of children is dependent on that size. (Although I hate to have a tool like this with such giant caveats)

Removing myself as an assignee since I'm not actively working on it right now. We should at least revisit this later after WinUI 3 ships--a lot of good commentary in here on making it easier for controls/UI to size itself intelligently based on locally-available space.

Was this page helpful?
0 / 5 - 0 ratings