講講我在Windows10(uwp)開發中遇到的一些坑.


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM