你的位置:首页 > ASP.net教程

[ASP.net教程]讲讲我在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 CurrentTemplate21 {22   get23   {24     if (CurrentViewModel == null)25     {26       return null;27     }28 29     return Untils.DataTemplateSelector.GetTemplate(CurrentViewModel);30   }31 }

DataTemplateSelector.GetTemplate是我们整个方法的核心.

1 public static class DataTemplateSelector2 {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         try102         {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 AysncUnhandledExceptionEventArgs119       {120         Exception = exception121       };122 123       UnhandledException(this, exWrapper);124 125 #if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION126       if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();127 #endif128       return exWrapper.Handled;129     }130 131     public event EventHandler<AysncUnhandledExceptionEventArgs> UnhandledException;132   }133 134   public class AysncUnhandledExceptionEventArgs : EventArgs135   {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   ExceptionHandlingSynchronizationContext13     .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 DEBUG38   if (System.Diagnostics.Debugger.IsAttached)39   {40     this.DebugSettings.EnableFrameRateCounter = true;41   }42 #endif43 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