7月29日發布的Windows10正式版,當天安裝好以后,在網絡不太好的情況下,經過多次嘗試終於裝上了Visual Studio 2015和Windows 10 10240的SDK.這兩周一直在開發UWP,講講在其中遇到的一些坑,不定時更新,有興趣的可以關注下.
1.DataType在UWP中缺失的問題
在WPF中使用過MVVMLight的都知道,我們可以在App.xaml文件中通過DataType將ViewModel和View綁定在一起.
1 <DataTemplate DataType="{x:Type vm:MyViewModel}"> 2 <views:MyView/> 3 </DataTemplate>
但是在Windows10(包括WP7等),是沒有DataType的屬性的,這意味着我們不能用這種方式來實現ViewModel和View的綁定.但是我們可以曲線救國一下,通過key的方式來尋找DataTemplate來綁定.
首先,我們需要改變我們在UWP中的寫法.
1 <DataTemplate x:Key="MyViewModel"> 2 <view:MyView/> 3 </DataTemplate>
然后我們在我們的MainPage.xaml文件中加入一個ContentControl.
1 <ContentControl Content="{Binding CurrentViewModel}" ContentTemplate="{Binding Path=CurrentTemplate}" />
我們的各個Views是用Usercontrol實現的.我們需要在MainPageViewModel中添加相應的綁定項.
1 public ViewModelBase CurrentViewModel 2 { 3 get 4 { 5 return currentViewModel; 6 } 7 set 8 { 9 if (currentViewModel == value) 10 { 11 return; 12 13 } 14 currentViewModel = value; 15 RaisePropertyChanged(()=>CurrentViewModel); 16 RaisePropertyChanged(()=>CurrentTemplate); 17 } 18 } 19 20 public DataTemplate CurrentTemplate 21 { 22 get 23 { 24 if (CurrentViewModel == null) 25 { 26 return null; 27 } 28 29 return Untils.DataTemplateSelector.GetTemplate(CurrentViewModel); 30 } 31 }
DataTemplateSelector.GetTemplate是我們整個方法的核心.
1 public static class DataTemplateSelector 2 { 3 public static DataTemplate GetTemplate(ViewModelBase param) 4 { 5 Type t = param.GetType(); 6 return App.Current.Resources[t.Name] as DataTemplate; 7 } 8 }
通過查找Key的方式將ViewModel和View綁定在一起,這樣就實現了我們的功能.我們接下來只要關注App.xaml或者相關文件中聲明我們的Key就行了.
2.附加屬性解決WebView不能直接綁定Html內容的問題
WebView的Source屬性只能綁定微軟規定的一些地址協議,不能直接綁定HTML的內容.通過附加屬性可以解決這個問題,利用的是NavigateToString方法
1 public static readonly DependencyProperty SourceStringProperty = 2 DependencyProperty.RegisterAttached("SourceString", typeof(string), typeof(Untils), new PropertyMetadata("", OnSourceStringChanged)); 3 4 public static string GetSourceString(DependencyObject obj) { return obj.GetValue(SourceStringProperty).ToString(); } 5 public static void SetSourceString(DependencyObject obj, string value) { obj.SetValue(SourceStringProperty, value); } 6 7 private static void OnSourceStringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 8 { 9 WebView wv = d as WebView; 10 if (wv != null) 11 { 12 wv.NavigateToString(e.NewValue.ToString()); 13 } 14 }
聲明一個SourceString的附加屬性,然后在變更事件中進行導航,然后Xaml文件中:
1 <WebView Property:Untils.SourceString="{Binding Url,Mode=TwoWay}"/>
這樣子,就可以直接綁定Html內容了.
3.異步(async)方法中的異常無法被App的UnhandledException捕獲的問題.
這是一個比較嚴重的問題.目前已知很多的做法就是局部try catch來解決這個問題.這樣做是很容易導致Process被強制終止然后引起閃退的問題的.
我這里用了一個線程同步模型類解決這個問題.
1 using System; 2 using System.Threading; 3 using Windows.UI.Xaml.Controls; 4 5 namespace AiJianShu.ExceptionHandler 6 { 7 internal class ExceptionHandlingSynchronizationContext : SynchronizationContext 8 { 9 /// <summary> 10 /// 注冊事件. 需要在OnLaunched和OnActivated事件中調用 11 /// </summary> 12 /// <returns></returns> 13 public static ExceptionHandlingSynchronizationContext Register() 14 { 15 var syncContext = Current; 16 if (syncContext == null) 17 throw new InvalidOperationException("Ensure a synchronization context exists before calling this method."); 18 19 20 var customSynchronizationContext = syncContext as ExceptionHandlingSynchronizationContext; 21 22 23 if (customSynchronizationContext == null) 24 { 25 customSynchronizationContext = new ExceptionHandlingSynchronizationContext(syncContext); 26 SetSynchronizationContext(customSynchronizationContext); 27 } 28 29 30 return customSynchronizationContext; 31 } 32 33 /// <summary> 34 /// 將線程的上下文綁定到特定的Frame上面 35 /// </summary> 36 /// <param name="rootFrame"></param> 37 /// <returns></returns> 38 public static ExceptionHandlingSynchronizationContext RegisterForFrame(Frame rootFrame) 39 { 40 if (rootFrame == null) 41 throw new ArgumentNullException("rootFrame"); 42 43 var synchronizationContext = Register(); 44 45 rootFrame.Navigating += (sender, args) => EnsureContext(synchronizationContext); 46 rootFrame.Loaded += (sender, args) => EnsureContext(synchronizationContext); 47 48 return synchronizationContext; 49 } 50 51 private static void EnsureContext(SynchronizationContext context) 52 { 53 if (Current != context) 54 SetSynchronizationContext(context); 55 } 56 57 58 private readonly SynchronizationContext _syncContext; 59 60 61 public ExceptionHandlingSynchronizationContext(SynchronizationContext syncContext) 62 { 63 _syncContext = syncContext; 64 } 65 66 67 public override SynchronizationContext CreateCopy() 68 { 69 return new ExceptionHandlingSynchronizationContext(_syncContext.CreateCopy()); 70 } 71 72 73 public override void OperationCompleted() 74 { 75 _syncContext.OperationCompleted(); 76 } 77 78 79 public override void OperationStarted() 80 { 81 _syncContext.OperationStarted(); 82 } 83 84 85 public override void Post(SendOrPostCallback d, object state) 86 { 87 _syncContext.Post(WrapCallback(d), state); 88 } 89 90 91 public override void Send(SendOrPostCallback d, object state) 92 { 93 _syncContext.Send(d, state); 94 } 95 96 97 private SendOrPostCallback WrapCallback(SendOrPostCallback sendOrPostCallback) 98 { 99 return state => 100 { 101 try 102 { 103 sendOrPostCallback(state); 104 } 105 catch (Exception ex) 106 { 107 if (!HandleException(ex)) 108 throw; 109 } 110 }; 111 } 112 113 private bool HandleException(Exception exception) 114 { 115 if (UnhandledException == null) 116 return false; 117 118 var exWrapper = new AysncUnhandledExceptionEventArgs 119 { 120 Exception = exception 121 }; 122 123 UnhandledException(this, exWrapper); 124 125 #if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION 126 if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break(); 127 #endif 128 return exWrapper.Handled; 129 } 130 131 public event EventHandler<AysncUnhandledExceptionEventArgs> UnhandledException; 132 } 133 134 public class AysncUnhandledExceptionEventArgs : EventArgs 135 { 136 public bool Handled { get; set; } 137 public Exception Exception { get; set; } 138 } 139 }
使用實例:
1 public App() 2 { 3 this.InitializeComponent(); 4 this.Suspending += OnSuspending; 5 6 this.UnhandledException += App_UnhandledException; 7 8 } 9 10 private void RegisterExceptionHandlingSynchronizationContext() 11 { 12 ExceptionHandlingSynchronizationContext 13 .Register() 14 .UnhandledException += SynchronizationContext_UnhandledException; 15 } 16 17 private async void App_UnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) 18 { 19 e.Handled = true; 20 21 await new MessageDialog("Application Unhandled Exception:\r\n" + e.Exception.Message) 22 .ShowAsync(); 23 } 24 25 private async void SynchronizationContext_UnhandledException(object sender, AysncUnhandledExceptionEventArgs e) 26 { 27 e.Handled = true; 28 29 await new MessageDialog("Synchronization Context Unhandled Exception:\r\n" + e.Exception.Message) 30 .ShowAsync(); 31 } 32 33 protected override void OnLaunched(LaunchActivatedEventArgs e) 34 { 35 RegisterExceptionHandlingSynchronizationContext(); 36 37 #if DEBUG 38 if (System.Diagnostics.Debugger.IsAttached) 39 { 40 this.DebugSettings.EnableFrameRateCounter = true; 41 } 42 #endif 43 44 Frame rootFrame = Window.Current.Content as Frame; 45 46 // 不要在窗口已包含內容時重復應用程序初始化, 47 // 只需確保窗口處於活動狀態 48 if (rootFrame == null) 49 { 50 // 創建要充當導航上下文的框架,並導航到第一頁 51 rootFrame = new Frame(); 52 53 rootFrame.NavigationFailed += OnNavigationFailed; 54 55 if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 56 { 57 //TODO: 從之前掛起的應用程序加載狀態 58 } 59 60 // 將框架放在當前窗口中 61 Window.Current.Content = rootFrame; 62 } 63 64 if (rootFrame.Content == null) 65 { 66 // 當導航堆棧尚未還原時,導航到第一頁, 67 // 並通過將所需信息作為導航參數傳入來配置 68 // 參數 69 rootFrame.Navigate(typeof(MainPage), e.Arguments); 70 } 71 // 確保當前窗口處於活動狀態 72 Window.Current.Activate(); 73 } 74 75 protected override void OnActivated(IActivatedEventArgs args) 76 { 77 RegisterExceptionHandlingSynchronizationContext(); 78 base.OnActivated(args); 79 }
這樣全局的異常就都能在App.xaml.cs文件中被捕獲,不會導致閃退.
參考資料:
http://www.codeproject.com/Articles/113152/Applying-Data-Templates-Dynamically-by-Type-in-WP
http://www.markermetro.com/2013/01/technical/handling-unhandled-exceptions-with-asyncawait-on-windows-8-and-windows-phone-8/
https://github.com/kiwidev/WinRTExceptions