首页 > Windows 10 开发日记(五)-- 当Binding遇到异步 -- 解决方案

Windows 10 开发日记(五)-- 当Binding遇到异步 -- 解决方案



前文再续,上一章提出了问题,本章提出了三种解决方案:

 

解决方案一:手动进行异步转换,核心思想:将binding做的事情放入CodeBehind

 

FilterItemControl.XAML:

    

  

FilterItemControl.cs

 /// /// 设置数据源/// /// public async void SetSource(Filter filter){if (filter != null){_filter = filter;// 使用WriteableBitmap有一个不好的点:必须要知道图片的大小WriteableBitmap result = new WriteableBitmap(768, 1280);var wbData = await MyFilterSDK.ProcessFilterAsync(filter);if(wbData != null){using (var bmpStream = result.PixelBuffer.AsStream()){bmpStream.Seek(0, SeekOrigin.Begin);bmpStream.Write(wbData, 0, (int)bmpStream.Length);}FilterImage.Source = result;}FilterName.Text = filter.FilterName;}}

为其设置数据源, FilterItemsControl.cs

/// /// 数据源发生变化/// /// 滤镜列表private void OnItemsSourceChanged(List filters){if(filters != null){Container.Children.Clear();foreach(var filter in filters){FilterItemControl itemcontrol = new FilterItemControl();itemcontrol.Width = ITEMWIDTH;itemcontrol.Height = ITEMHEIGHT;// 将binding中做的事情放到代码中!itemcontrol.SetSource(filter);

itemcontrol.ItemSelected += Itemcontrol_ItemSelected;itemcontrol.ItemDoubleClicked += Itemcontrol_ItemDoubleClicked;Container.Children.Add(itemcontrol);}}}

  优点:方便简单

      缺点:XAML必须写死,没有扩展性,如果同样的数据变换一种显示方式,需要重写一个控件。

 

解决方案二:使用异步属性,核心思想,使用Binding和异步加载

 

FilterItemControl.xaml

    

FilterItemControl.cs不需要额外的东西,但是Model层的数据源需要增加一个异步属性用于被View层绑定

Filter.cs

        private AsyncProperty _wbAsyncProperty; // 异步属性public AsyncProperty WBAsyncProperty{get{return _wbAsyncProperty;}set{SetProperty(ref _wbAsyncProperty, value);}} 


     // 初始化public Filter(){WBAsyncProperty = new AsyncProperty(async () =>{var result = await MyFilterSDK.ProcessFilterAsync(this);return result;});}

  

由于返回值是byte[]类型,所以我们在binding时,必须要进行一次转换,将其转换为WriteableBitmap:BytesToImageConverter.cs

public class BytesToImageConverter : IValueConverter{public object Convert(object value, Type targetType, object parameter, string language){// 使用WriteableBitmap有一个不好的点:必须要知道图片的大小WriteableBitmap result = new WriteableBitmap(768, 1280);var filterData = value as byte[];if (filterData != null){#region  WriteableBitmap方案using (var bmpStream = result.PixelBuffer.AsStream()){bmpStream.Seek(0, SeekOrigin.Begin);bmpStream.Write(filterData, 0, (int)bmpStream.Length);return result;}#endregion}elsereturn null;}public object ConvertBack(object value, Type targetType, object parameter, string language){throw new NotImplementedException();}}

  关于如何实现AsyncProperty和其工作原理在这里不做深究,在这里总结一下这个方案的优缺点:

优点:使用Binding,UI上不会卡顿,图片获取完之后会显示在UI上

缺点: 1. 控件重用性不高

        2. SDK必须与UI无关,这也是为什么返回byte[],而不是直接返回WrieableBitmap的原因,与AsyncProperty的实现技术有关

        3. 因为原因2,必须实现转换器

 

解决方案三:使用DataTemplate,核心思想:将DataTemplate转换放到CodeBehind

 FilterItemControl.XAML需要改变,这里只需要一个ContentPresenter接收内容

     

FilterItemControl.cs需要增加一个ContentTemplate,用于获取应用在这个控件上的模板,并且根据模板把UI显示出来:

   public DataTemplate ContentDataTemplate{get { return (DataTemplate)GetValue(ContentDataTemplateProperty); }set { SetValue(ContentDataTemplateProperty, value); }}public static readonly DependencyProperty ContentDataTemplateProperty =DependencyProperty.Register("ContentDataTemplate", typeof(DataTemplate), typeof(FilterItemControl3), new PropertyMetadata(null,OnContentDataTemplateChanged));private static void OnContentDataTemplateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args){FilterItemControl3 owner = sender as FilterItemControl3;owner.OnContentDataTemplateChanged(args.NewValue as DataTemplate);}private async void OnContentDataTemplateChanged(DataTemplate newDataTemplate){UIElement rootElement = newDataTemplate.LoadContent() as UIElement;if(rootElement != null){Image img = VisualTreeExtensions.FindFirstElementInVisualTree(rootElement);if (img != null){#region 使用SDK 处理WriteableBitmap result = new WriteableBitmap(768, 1280);var wbData = await MyFilterSDK.ProcessFilterAsync(this.DataContext as Filter);if (wbData != null){using (var bmpStream = result.PixelBuffer.AsStream()){bmpStream.Seek(0, SeekOrigin.Begin);bmpStream.Write(wbData, 0, (int)bmpStream.Length);}img.Source = result;}#endregion// 改变了图片之后,需要将其加入到可视化中以显示,如果不加这一步你可以想象会出现什么情况Presenter.Content = rootElement;}}}

同样的,需要修改FilterItemsControl.cs,增加一个ItemDataTemplate传递给FilterItemControl:

        /// /// 子项的模板/// public DataTemplate ItemDataTemplate{get { return (DataTemplate)GetValue(ItemDataTemplateProperty); }set { SetValue(ItemDataTemplateProperty, value); }}public static readonly DependencyProperty ItemDataTemplateProperty =DependencyProperty.Register("ItemDataTemplate", typeof(DataTemplate), typeof(FilterItemsControl3), new PropertyMetadata(0));/// /// 数据源发生变化/// /// 滤镜列表private void OnItemsSourceChanged(List filters){if (filters != null){Container.Children.Clear();foreach (var filter in filters){FilterItemControl3 itemcontrol = new FilterItemControl3();//itemcontrol.Width = ITEMWIDTH; // 不要了,在DataTemplate中指定//itemcontrol.Height = ITEMHEIGHT;//1. 设置DataContextitemcontrol.DataContext = filter;//2. 设置模板itemcontrol.ContentDataTemplate = ItemDataTemplate;itemcontrol.ItemSelected += Itemcontrol_ItemSelected;itemcontrol.ItemDoubleClicked += Itemcontrol_ItemDoubleClicked;Container.Children.Add(itemcontrol);}}}

那么我们只需要在使用这个控件的地方编写一个ItemDataTemplate就可以了:


 第三种方案是我想表达的,但是我们看出来,它也并不是最优的,需要在代码中取出DataTemplate中的可视元素,然后将SDK处理过的图片放到目标Image控件的Source中去,但是他的优点也是有的: 1. UI的可扩展性

                             2. 与异步无关的属性可以通过Binding展示

可以说,方案三模拟了DataTemplate如何应用在一个控件上的,这也是我想从这个例子中总结的东西:

1. DataTemplate的作用

2. 控件在应用了DataTemplate之后发生了什么?

3. 通过DataTemplate.LoadContent(), 获取控件,并且修改控件,如果不使用Presenter.Content = rootElement, 为什么没有反应?

 

总结:

1. 首先DataTemplate的MSDN的解释非常清楚,就是将“数据"转换为可见的元素,这也是为什么我们选择DataTemplate来展示Filter的原因。

2. 控件在应用了DataTemplate之后会发生什么?因为微软的封闭,我们看不到,但是可以猜到,它的实现类似于我们方案三的实现:取得DataTemplate中的元素,并且将其加载到可视化树中显示。我们在XAML中写的DataTemplate类似于一个类的声明,当某个控件需要这个DataTemplate时,会new 一个实例,然后目标控件,并且替换它之前的可视化树。

3. 第三个问题的答案基于第二个问题:通过DataTemplate.LoadContent()获得的UIElement每次都是不一样的,就是说调用该方法就类似与调用 new DataTemplate(),一样,只是一次实例化,此时的元素并没有加载到可视化树中(可以通过GetHashCode()对比),所以,无论做什么修改,你都看不出结果。所以必须要有Presenter.Content = rootElement这关键的一步。

 

Demo已经写好,VS2015工程,WU框架,PC运行。

MyFilterDemo.rar

转载于:https://www.cnblogs.com/DinoWu/p/4549770.html

更多相关:

  • 不BB写在自己博客园看的舒服     RelativeLayout布局 android:layout_marginTop="25dip" //顶部距离 android:gravity="left" //空间布局位置 android:layout_marginLeft="15dip //距离左边距 // 相对于给定ID控件 andro...

  • top.geometry()设定窗口的初始大小 scale.set()设定滑块的初始值 scale.get()获取滑块变化的值 控件通过回调函数与其他控件进行通信(Label控件中的文本会受到Scale控件上操作的影响) 转载于:https://www.cnblogs.com/TmHm/p/9949947.html...

  • 在一些控件里的keydown方法,没有办法捕获所有的按键消息 比如自己写一个窗体控件库,继承了UserControl 但是没有办法捕获一些键,比如方向键等 所以必须重载 processDialogkey 方法 processDialogkey 的描述 在msdn中是这样的   在消息预处理过程中调用此方法,以处理对话框字符,比如 Ta...

  • 六 PetShop之表示层设计 表示层(Presentation Layer)的设计可以给系统客户最直接的体验和最十足的信心。正如人与人的相交相识一样,初次见面的感觉总是永难忘怀的。一件交付给客户使用的产品,如果在用户界面(User Interface,UI)上缺乏吸引人的特色,界面不友好,操作不够体贴,即使这件产品性能非常优异,架...

  • ASP.NET页生命周期的定义,有以下8个方面:页请求,开始,页初始化,页加载,验证,回发事件,呈现,卸载。 ASP.NET 页运行时,此页将经历一个生命周期,在生命周期中将执行一系列处理步骤。这些步骤包括初始化、实例化控件、还原和维护状态、运行事件处理程序代码以及进行呈现。了解页的生命周期非常重要,这样就能在合适的生命周期阶段编写代...