Prevent UWP error/crash when updating binding off the main thread; fixes #8167 (...
authorE.Z. Hart <hartez@users.noreply.github.com>
Fri, 1 Nov 2019 20:20:32 +0000 (14:20 -0600)
committerGitHub <noreply@github.com>
Fri, 1 Nov 2019 20:20:32 +0000 (14:20 -0600)
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8167.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Platform.UAP/Dispatcher.cs
Xamarin.Forms.Platform.UAP/DispatcherProvider.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8167.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8167.cs
new file mode 100644 (file)
index 0000000..5bdadff
--- /dev/null
@@ -0,0 +1,123 @@
+using System.ComponentModel;
+using System.Threading.Tasks;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.Forms.Core.UITests;
+using Xamarin.UITest;
+using NUnit.Framework;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+       [NUnit.Framework.Category(UITestCategories.ManualReview)]
+#endif
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.Github, 8167, "[Bug] XF 4.3 UWP Crash - Element not found", PlatformAffected.UWP)]
+       public class Issue8167 : TestContentPage
+       {
+               const string Run = "Update Text";
+               const string Success = "Success";
+
+               protected override void Init()
+               {
+                       var layout = new StackLayout();
+
+                       var instructions = new Label
+                       {
+                               Text = $"Tap the button marked {Run}. If the Label below reads {Success} then the test has passed."
+                       };
+
+                       layout.Children.Add(instructions);
+
+                       var label = new Label();
+                       label.SetBinding(Label.TextProperty, new Binding(nameof(_8167ViewModel.Text)));
+
+                       layout.Children.Add(label);
+
+                       var presenter = new ContentPresenter();
+                       presenter.SetBinding(ContentPresenter.WidthRequestProperty, new Binding(nameof(_8167ViewModel.Width)));
+
+                       layout.Children.Add(presenter);
+
+                       var model = new _8167ViewModel();
+
+                       var button = new Button() { Text = Run };
+
+                       button.Clicked += (obj, args) =>
+                       {
+                               model.UpdateText();
+                               model.UpdateWidth();
+                       };
+
+                       layout.Children.Add(button);
+
+                       Content = layout;
+
+                       BindingContext = model;
+               }
+
+               [Preserve(AllMembers = true)]
+               public class _8167ViewModel : INotifyPropertyChanged
+               {
+                       string _text;
+                       double _width;
+
+                       public _8167ViewModel()
+                       {
+                               _text = "Starting value";
+                       }
+
+                       public void UpdateText()
+                       {
+                               Task.Run(() => { Text = Success; });
+                       }
+
+                       public void UpdateWidth()
+                       {
+                               Task.Run(() => { Width = 200; });
+                       }
+
+                       public string Text
+                       {
+                               get => _text;
+                               set
+                               {
+                                       _text = value;
+                                       RaisePropertyChanged(nameof(Text));
+                               }
+                       }
+
+                       public double Width
+                       {
+                               get => _width;
+                               set
+                               {
+                                       _width = value;
+                                       RaisePropertyChanged(nameof(Width));
+                               }
+                       }
+
+                       public event PropertyChangedEventHandler PropertyChanged;
+
+                       void RaisePropertyChanged(string propertyName)
+                       {
+                               PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+                       }
+               }
+
+#if UITEST
+               [Test]
+               public void ThreadpoolBindingUpdateShouldNotCrash()
+               {
+                       RunningApp.WaitForElement(Run);
+                       RunningApp.Tap(Run);
+                       RunningApp.WaitForElement(Success);
+               }
+#endif
+       }
+
+
+}
index 32f55d7..dd59c33 100644 (file)
@@ -94,6 +94,7 @@
     <Compile Include="$(MSBuildThisFileDirectory)Issue7803.xaml.cs">
       <DependentUpon>Issue7803.xaml</DependentUpon>
     </Compile>
+    <Compile Include="$(MSBuildThisFileDirectory)Issue8167.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)RefreshViewTests.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue7338.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)ScrollToGroup.cs" />
index fbc9189..5cad1e3 100644 (file)
@@ -20,6 +20,11 @@ namespace Xamarin.Forms.Platform.UWP
                        _coreDispatcher = CoreApplication.GetCurrentView().Dispatcher;
                }
 
+               public Dispatcher(CoreDispatcher coreDispatcher) 
+               {
+                       _coreDispatcher = coreDispatcher;
+               }
+
                bool IDispatcher.IsInvokeRequired => Device.IsInvokeRequired;
        }
 }
index 549a705..f41a0f5 100644 (file)
@@ -1,10 +1,5 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Xamarin.Forms;
-using Xamarin.Forms.Internals;
 using Xamarin.Forms.Platform.UWP;
 
 [assembly: Dependency(typeof(DispatcherProvider))]
@@ -17,6 +12,26 @@ namespace Xamarin.Forms.Platform.UWP
 
                public IDispatcher GetDispatcher(object context)
                {
+                       if (s_current != null)
+                       {
+                               return s_current;                       
+                       }
+
+                       if (context is BindableObject)
+                       {
+                               if (context is VisualElement element)
+                               {
+                                       var renderer = Platform.GetRenderer(element);
+                                       if (renderer?.ContainerElement != null)
+                                       {
+                                               s_current = new Dispatcher(renderer.ContainerElement.Dispatcher);
+                                               return s_current;
+                                       }
+                               }
+
+                               return null;
+                       }
+                       
                        return s_current = s_current ?? new Dispatcher();
                }
        }