If you set a specific value on a Slider that is specifically sized, the UWP app crashes with:
Exception: Windows.UI.Xaml.LayoutCycleException
_Message: Layout cycle detected. Layout could not complete.\r\nLayout cycle detected. Layout could not complete._
I can confirm that the app crashes on the Surface Pro 6 with a screen scale value of 175% but I think it crashes also on displays with a scale value set over 100% (e.g. 125%, 150%). The app does not crash if the screen scale value is set to 100%, so it has something with it to do.
Steps to reproduce:
I already reported this issue on the MSDN forums:
See here
Thanks for reporting this issue, we'll debug it!
Hi, I was the one who reported this on UserVoice. I will try to do some more investigation in the meantime, but I wanted to reiterate here that I think this is a very serious issue with the layout system which really needs fixing, and does not just apply to the slider.
I think it has to do with the process of rounding during the layout process. Between the measure pass and the arrange pass, values seem to be rounded to the nearest display pixel (this is less likely to change things on a 100% scaled display hence the reason for not seeing the error there). However, this process of rounding is unstable. In my case, a value of 365 was being alternately rounded to 365.333333 and 364.66666666, causing the SizeChanged event on child controls to fire, and because the slider readjusts its thumb position when its size changes, this causes another measure pass and thus a loop. This instability in the layout process is a severe problem that could cause a layout cycle in many situations, but it depends on display scaling etc so will not necessarily be picked up during debugging, making it even worse.
I'm not sure what the best fix is. My suggestion is, at the point where a 'desired length' is rounded to display pixels, it should cache a copy of the desired length and the rounded length. Then, the next time, if the desired length hasn't changed by more than a small amount, ignore it and use the cached rounded length.
Of course, this is just conjecture so I don't know if that would fix the problem. It would also be interesting to know why the rounding is alternating up and down. However, it's obvious that if the value is exactly between two possible rounded values then the rounding is very sensitive to any tiny change in the value - if the value is computed via a sum or something, then perhaps even changes to the order of the sum could change the rounded result.
As an aside, the fact that measure tends not to use rounded pixels, whereas arrange does, is slightly problematic for controls that wrap content onto multiple lines. The measure pass may allocate too much or not enough vertical space depending on the width being rounded up/down.
Also, I'd like to add that I wrote my own slider class as a workaround, using a RenderTransform to move the thumb, which doesn't cause a measure pass. Interestingly in my first attempt I adjusted the margin instead and got the exact same layout cycle bug as the built-in control has.
If anyone wants the code for my custom slider (it's not very much code, and doesn't support some of the more complicated features) they are welcome to have it - just ask.
@benstevens48 Can you share the code of your slider (maybe as a Gist)?
And yes it is true that this problem is not Slider specific, you can for example change the slider in my demo project to a ProgressBar and it would also crash.
@hig-dev here it is - https://gist.github.com/benstevens48/8ad13f1a8f62302af06667da2f885e82. Note that I've extracted it straight from my app so the template may need modifying slightly - I've made a note about that in the Gist.
@benstevens48 Thank you for the workaround, I added support for the thumb tooltip and I am just using the default Slider style. https://gist.github.com/hig-dev/49154bc603c77a1925d586244da90f7c
Great! Does it really work with the default style? I made some changes to the HorizontalTemplate and VerticalTemplate parts due to using a render transform to move the thumb and set the filled-in rectangle width, and I wouldn't expect it to work without updating the template for these bits.
@benstevens48 As far as I know it works, I didn't see an issue.
A user on the MSDN forums suggested this fix:
private Size oldSize, measuredSize;
protected override Size MeasureOverride(Size availableSize)
{
if (!Size.Equals(oldSize, availableSize))
{
measuredSize = base.MeasureOverride(availableSize);
oldSize = availableSize;
}
return measuredSize;
}
Maybe this helps to fix the problem. Please fix this problem, we shouldn't be forced to use a workaround for months.
This is the most likely cause of these crashes.
I tried several different scale factors but could not get the app to crash on recent windows builds. Which version of windows are you seeing this crash in ? Is there anything else I might be missing for it to repro ? Thanks.
I just tried the sample app on my PC (I hadn't tried it before) and it ran fine at 150% display scaling, but crashed immediately at 175% display scaling, as stated by the original poster. I'm running Windows 10 version 1903.
Thanks I am able to repro on a physical device with 175% scale factor.
Fixed in the platform codebase.
I've experienced this issue with a Slider on the WinUI 3 Preview. To confirm that it is scale factor, I have two screens, with one set to 125% Scale and the other at 100%. When the application is on the 100% screen, I can move the slider fine, but as soon as I move it to the other one, the application crashes as soon as I move the thumb to the far right.
I don't really understand why this has to be fixed in WinUI3, this is a such bad-behavior bug that we can't even catch the exception. All my apps that are using Slider will crash any moment. Can it be fixed in WinUI2?
@XeonKHJ If this is a Slider
specific bug (or an underlying framework bug), this cannot be fixed in WinUI 2 as the Slider control code is not in WinUI 2 but still in the underlying OS. Any fix in its code would have to happen in the framework in the OS and be serviced as a Windows Update to all the affected Windows versions still supported.
Any idea that whether the team should be working on this issue knowing this problem and working on it?
This issue is owned by the WinUI team which worked on the OS XAML platform before WinUI 2. Which is also the reason why this bug will presumably be fixed in WinUI 3 (not necessarily WinUI 3.0 but potentially subsequent WinUI 3.x releases). If you are still seeing this issue using the OS XAML Slider control that sounds like some more waiting for you then, unfortunately. Not sure if a reasonable workaround can be found until WinUI 3?
@JesseCol, @llongley This issue was fixed in the platform codebase but it looks like that commit is not in the WinUI3 codebase. Can we port the fix over ?
Which version of Windows supposedly has the fix? This happens to me using latest WinUI 2.4.0 and Windows 10 1903...
@robloo The fix is in 2004 and has been ported to WinUI3 as well, so should be included WinUI3 preview2.
The fix is in WinUI3 Preview2. Thanks!
@ranjeshj is this bug flagged for servicing down-level OSes? Seems like it effected quite a few number of folks.
Controls issues very rarely meet the bar for servicing fixes, I would be very surprised if this was serviced.
Controls issues very rarely meet the bar for servicing fixes, I would be very surprised if this was serviced.
That right there is one of the top most issues that stopped widespread UWP adoption. It was never as stable as WPF out the door and these types of problems occur so much it creates a lot of hurdles for development. WinUI 3.0 can't come soon enough!
WinUI 3.0 can't come soon enough!
Definitely!
Most helpful comment
Hi, I was the one who reported this on UserVoice. I will try to do some more investigation in the meantime, but I wanted to reiterate here that I think this is a very serious issue with the layout system which really needs fixing, and does not just apply to the slider.
I think it has to do with the process of rounding during the layout process. Between the measure pass and the arrange pass, values seem to be rounded to the nearest display pixel (this is less likely to change things on a 100% scaled display hence the reason for not seeing the error there). However, this process of rounding is unstable. In my case, a value of 365 was being alternately rounded to 365.333333 and 364.66666666, causing the SizeChanged event on child controls to fire, and because the slider readjusts its thumb position when its size changes, this causes another measure pass and thus a loop. This instability in the layout process is a severe problem that could cause a layout cycle in many situations, but it depends on display scaling etc so will not necessarily be picked up during debugging, making it even worse.
I'm not sure what the best fix is. My suggestion is, at the point where a 'desired length' is rounded to display pixels, it should cache a copy of the desired length and the rounded length. Then, the next time, if the desired length hasn't changed by more than a small amount, ignore it and use the cached rounded length.
Of course, this is just conjecture so I don't know if that would fix the problem. It would also be interesting to know why the rounding is alternating up and down. However, it's obvious that if the value is exactly between two possible rounded values then the rounding is very sensitive to any tiny change in the value - if the value is computed via a sum or something, then perhaps even changes to the order of the sum could change the rounded result.
As an aside, the fact that measure tends not to use rounded pixels, whereas arrange does, is slightly problematic for controls that wrap content onto multiple lines. The measure pass may allocate too much or not enough vertical space depending on the width being rounded up/down.
Also, I'd like to add that I wrote my own slider class as a workaround, using a RenderTransform to move the thumb, which doesn't cause a measure pass. Interestingly in my first attempt I adjusted the margin instead and got the exact same layout cycle bug as the built-in control has.
If anyone wants the code for my custom slider (it's not very much code, and doesn't support some of the more complicated features) they are welcome to have it - just ask.