讲讲我在Windows10(uwp)开发中遇到的一些坑

7月29日发布的Windows10正式版,当天安装好以后,在网络不太好的情况下,经过多次尝试终于装上了Visual Studio 2015和Windows 10 10240的SDK.这两周一直在开发UWP,讲讲在其中遇到的一些坑,不定时更新,有兴趣的可以关注下.

1.DataType在UWP中缺失的问题

在WPF中使用过MVVMLight的都知道,我们可以在App.xaml文件中通过DataType将ViewModel和View绑定在一起.



    

但是在Windows10(包括WP7等),是没有DataType的属性的,这意味着我们不能用这种方式来实现ViewModel和View的绑定.但是我们可以曲线救国一下,通过key的方式来寻找DataTemplate来绑定.

首先,我们需要改变我们在UWP中的写法.


   



然后我们在我们的MainPage.xaml文件中加入一个ContentControl.

 


我们的各个Views是用Usercontrol实现的.我们需要在MainPageViewModel中添加相应的绑定项.

public ViewModelBase CurrentViewModel
{
     get
    {
         return currentViewModel;
    }
     set
    {
        if (currentViewModel == value)
        {
             return;

        }
         currentViewModel = value;
        RaisePropertyChanged(()=>CurrentViewModel);
         RaisePropertyChanged(()=>CurrentTemplate);
     }
 }
 
public DataTemplate CurrentTemplate
 {
     get
    {
         if (CurrentViewModel == null)
         {
             return null;
         }
 
         return Untils.DataTemplateSelector.GetTemplate(CurrentViewModel);
     }
 }




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

public static class DataTemplateSelector
{
    public static DataTemplate GetTemplate(ViewModelBase param)
     {
         Type t = param.GetType();
         return App.Current.Resources[t.Name] as DataTemplate;
     }
 }



通过查找Key的方式将ViewModel和View绑定在一起,这样就实现了我们的功能.我们接下来只要关注App.xaml或者相关文件中声明我们的Key就行了.


2.附加属性解决WebView不能直接绑定Html内容的问题


WebView的Source属性只能绑定微软规定的一些地址协议,不能直接绑定HTML的内容.通过附加属性可以解决这个问题,利用的是NavigateToString方法


public static readonly DependencyProperty SourceStringProperty =
DependencyProperty.RegisterAttached("SourceString", typeof(string), typeof(Untils), new PropertyMetadata("", OnSourceStringChanged));

 public static string GetSourceString(DependencyObject obj) { return obj.GetValue(SourceStringProperty).ToString(); }
public static void SetSourceString(DependencyObject obj, string value) { obj.SetValue(SourceStringProperty, value); }
 
 private static void OnSourceStringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
 {
     WebView wv = d as WebView;
    if (wv != null)
     {
        wv.NavigateToString(e.NewValue.ToString());
     }
 }

声明一个SourceString的附加属性,然后在变更事件中进行导航,然后Xaml文件中:


  

这样子,就可以直接绑定Html内容了.

3.异步(async)方法中的异常无法被App的UnhandledException捕获的问题.

这是一个比较严重的问题.目前已知很多的做法就是局部try catch来解决这个问题.这样做是很容易导致Process被强制终止然后引起闪退的问题的.

我这里用了一个线程同步模型类解决这个问题.


 using System;
 using System.Threading;
 using Windows.UI.Xaml.Controls;
 
 namespace AiJianShu.ExceptionHandler
  {
     internal class ExceptionHandlingSynchronizationContext : SynchronizationContext
     {
        /// 
        /// 注册事件.  需要在OnLaunched和OnActivated事件中调用
         /// 
        /// 
        public static ExceptionHandlingSynchronizationContext Register()
        {
             var syncContext = Current;
             if (syncContext == null)
               throw new InvalidOperationException("Ensure a synchronization context exists before calling this method.");
 
 
             var customSynchronizationContext = syncContext as ExceptionHandlingSynchronizationContext;


             if (customSynchronizationContext == null)
             {
                 customSynchronizationContext = new ExceptionHandlingSynchronizationContext(syncContext);
                SetSynchronizationContext(customSynchronizationContext);
             }


            return customSynchronizationContext;
        }

         /// 
          /// 将线程的上下文绑定到特定的Frame上面
         /// 
         /// 
         /// 
         public static ExceptionHandlingSynchronizationContext RegisterForFrame(Frame rootFrame)
         {
             if (rootFrame == null)
                 throw new ArgumentNullException("rootFrame");
 
           var synchronizationContext = Register();
 
            rootFrame.Navigating += (sender, args) => EnsureContext(synchronizationContext);
             rootFrame.Loaded += (sender, args) => EnsureContext(synchronizationContext);

            return synchronizationContext;
        }

        private static void EnsureContext(SynchronizationContext context)
         {
            if (Current != context)
                 SetSynchronizationContext(context);
         }
 
 
        private readonly SynchronizationContext _syncContext;
 
 
         public ExceptionHandlingSynchronizationContext(SynchronizationContext syncContext)
         {
             _syncContext = syncContext;
         }
 
 
        public override SynchronizationContext CreateCopy()
         {
             return new ExceptionHandlingSynchronizationContext(_syncContext.CreateCopy());
        }
 
 
         public override void OperationCompleted()
         {
             _syncContext.OperationCompleted();
        }

 
         public override void OperationStarted()
         {
             _syncContext.OperationStarted();
        }

 
         public override void Post(SendOrPostCallback d, object state)
         {
           _syncContext.Post(WrapCallback(d), state);
         }
 
 
         public override void Send(SendOrPostCallback d, object state)
       {
             _syncContext.Send(d, state);
         }
 
 
         private SendOrPostCallback WrapCallback(SendOrPostCallback sendOrPostCallback)
        {
             return state =>
            {
                 try
                {
                     sendOrPostCallback(state);
                }
                catch (Exception ex)
                 {
                     if (!HandleException(ex))
                         throw;
                 }
             };
        }
 
        private bool HandleException(Exception exception)
         {
             if (UnhandledException == null)
                 return false;
 
             var exWrapper = new AysncUnhandledExceptionEventArgs
             {
                 Exception = exception
             };

             UnhandledException(this, exWrapper);
 
 #if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
             if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();
 #endif
             return exWrapper.Handled;
         }
 
         public event EventHandler UnhandledException;
     }
 
     public class AysncUnhandledExceptionEventArgs : EventArgs
     {
         public bool Handled { get; set; }
         public Exception Exception { get; set; }
     }
 }


使用实例:


public App()
 {
     this.InitializeComponent();
     this.Suspending += OnSuspending;
 
     this.UnhandledException += App_UnhandledException;

}
 
 private void RegisterExceptionHandlingSynchronizationContext()
 {
    ExceptionHandlingSynchronizationContext
         .Register()
         .UnhandledException += SynchronizationContext_UnhandledException;
 }
 
 private async void App_UnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
 {
     e.Handled = true;
 
     await new MessageDialog("Application Unhandled Exception:\r\n" + e.Exception.Message)
         .ShowAsync();
 }

 private async void SynchronizationContext_UnhandledException(object sender, AysncUnhandledExceptionEventArgs e)
 {
     e.Handled = true;
 
     await new MessageDialog("Synchronization Context Unhandled Exception:\r\n" + e.Exception.Message)
         .ShowAsync();
 }
 
 protected override void OnLaunched(LaunchActivatedEventArgs e)
 {
     RegisterExceptionHandlingSynchronizationContext();

 #if DEBUG
     if (System.Diagnostics.Debugger.IsAttached)
     {
         this.DebugSettings.EnableFrameRateCounter = true;
    }
 #endif
 
    Frame rootFrame = Window.Current.Content as Frame;
 
     // 不要在窗口已包含内容时重复应用程序初始化,
     // 只需确保窗口处于活动状态
     if (rootFrame == null)
     {
        // 创建要充当导航上下文的框架,并导航到第一页
        rootFrame = new Frame();

        rootFrame.NavigationFailed += OnNavigationFailed;

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
         {
             //TODO: 从之前挂起的应用程序加载状态
        }

         // 将框架放在当前窗口中
        Window.Current.Content = rootFrame;
    }

     if (rootFrame.Content == null)
     {
         // 当导航堆栈尚未还原时,导航到第一页,
         // 并通过将所需信息作为导航参数传入来配置
         // 参数
        rootFrame.Navigate(typeof(MainPage), e.Arguments);
     }
     // 确保当前窗口处于活动状态
    Window.Current.Activate();
 }

 protected override void OnActivated(IActivatedEventArgs args)
 {
     RegisterExceptionHandlingSynchronizationContext();
     base.OnActivated(args);
 }

这样全局的异常就都能在App.xaml.cs文件中被捕获,不会导致闪退.

来自开发者交流群:53078485,期待您的加入!

你可能感兴趣的:(Windows,随笔)