当前位置: 首页 > 编程日记 > 正文

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


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

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

FilterItemControl.XAML:

    <Grid><Image x:Name="FilterImage" Stretch="UniformToFill"/><Grid VerticalAlignment="Bottom" Height="20"><TextBlock x:Name="FilterName" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/></Grid><Border x:Name="border" BorderBrush="White" BorderThickness="1" d:LayoutOverrides="LeftPosition, RightPosition, TopPosition, BottomPosition" Margin="1" Visibility="Collapsed"/></Grid>

FilterItemControl.cs

 /// <summary>/// 设置数据源/// </summary>/// <param name="filter"></param>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

/// <summary>/// 数据源发生变化/// </summary>/// <param name="filters">滤镜列表</param>private void OnItemsSourceChanged(List<Filter> 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

    <Grid><Image x:Name="FilterImage" Stretch="UniformToFill" Source="{Binding WBAsyncProperty.AsyncValue, Converter={StaticResource imagConverter}}"/><Grid VerticalAlignment="Bottom" Height="20"><TextBlock x:Name="FilterName" TextWrapping="Wrap" Text="{Binding FilterName}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/></Grid><Border x:Name="border" BorderBrush="White" BorderThickness="1" d:LayoutOverrides="LeftPosition, RightPosition, TopPosition, BottomPosition" Margin="1" Visibility="Collapsed"/></Grid>

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

Filter.cs

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

     // 初始化public Filter(){WBAsyncProperty = new AsyncProperty<byte[]>(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接收内容

   <Grid>  
<ContentPresenter x:Name="Presenter"/><Border x:Name="border" BorderBrush="White" BorderThickness="1" d:LayoutOverrides="LeftPosition, RightPosition, TopPosition, BottomPosition" Margin="1" Visibility="Collapsed"/></Grid>

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<Image>(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:

        /// <summary>/// 子项的模板/// </summary>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));/// <summary>/// 数据源发生变化/// </summary>/// <param name="filters">滤镜列表</param>private void OnItemsSourceChanged(List<Filter> 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就可以了:

<local:FilterItemsControl3 x:Name="FilterItemsUserControl" Opacity="0" RenderTransformOrigin="0.5,0.5" Margin="0"><local:FilterItemsControl3.RenderTransform><CompositeTransform TranslateY="100"/></local:FilterItemsControl3.RenderTransform><local:FilterItemsControl3.ItemDataTemplate><DataTemplate><Grid Width="80" Height="80"><Image x:Name="SourceImage"/><Grid Height="20" VerticalAlignment="Top" Background="#7F000000"><TextBlock x:Name="textBlock" TextWrapping="Wrap" Text="{Binding FilterName}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/></Grid></Grid></DataTemplate></local:FilterItemsControl3.ItemDataTemplate></local:FilterItemsControl3>

第三种方案是我想表达的,但是我们看出来,它也并不是最优的,需要在代码中取出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

相关文章:

java实现局域网内单对单和多对多通信的设计思路

这个看起来是很简单的&#xff0c;不就是socket吗&#xff0c;但是&#xff0c;要想有一个好的用户体验&#xff0c;还是很难的&#xff0c;主要问题有&#xff1a; 既然是局域网&#xff0c;那么就必然没有一个固定的IP地址作为主机&#xff0c;这个问题怎么解决&#xff1f; …

GIT - 拉取其他仓库的某个分支的代码

场景 在A仓库拉取B仓库develop分支的代码到A的本地分支 步骤 查看本地已有的远程仓库git remote // 查看远程仓库的名字 发现只有A-v 更加详细的远程仓库说明 复制代码添加远程仓库git remote add BName Bgiturl // 添加远程仓库 BNname别名 Bgiturl远程的git地址 复制代码添…

fetchMetadata: sill install loadAllDepsIntoIdealTree

在搭建vue项目的时候出现报错如下 fetchMetadata: sill install loadAllDepsIntoIdealTree 先输入 npm install -g cnpm --registryhttps://registry.npm.taobao.org 在输入cnpm install

redmine忘记username和password

环境&#xff1a; Ubuntu 13.10 bitnami-redmine-2.5.1-1-linux-x64-installer.run 用bitnami安装完redmine以后&#xff0c;有是否忘记了username和password。可是又没有配置邮件server&#xff0c;无法找回password。 仅仅能去server更改了。更改过程例如以下&#xff1a; 1…

SpringBoot b2b2c 多用户商城系统(十五)Springboot整合RabbitMQ...

这篇文章带你了解怎么整合RabbitMQ服务器&#xff0c;并且通过它怎么去发送和接收消息。我将构建一个springboot工程&#xff0c;通过RabbitTemplate去通过MessageListenerAdapter去订阅一个POJO类型的消息。 准备工作15minIDEAmaven 3.0在开始构建项目之前&#xff0c;机器需要…

SQLite 日期类型(转)

SQLite日期类型 简单示例: SELECT datetime(CHANGE_DATE,localtime), strftime(%Y-%m-%d,CHANGE_DATE,localtime), datetime(now,localtime), strftime(%Y-%m-%d,now,localtime), DATE(now,localtime), time(now,Localtime), time(2010-11-27 01:…

Visual Studio Code常用插件

名称 功能 Chinese (Simplified) Language Pack for Visual Studio Code 汉化 VSCode Auto Close Tag 自动为写的html标签进行闭 合 Beautify 代码格式化 Element UI Snippets Element UI的代码提示功能 Material Icon Theme 为项目换上相应的图标 Vetur对vue代码进行高亮 View…

sqlserver导入excel的电话号码(身份证)变为科学计数解决方式

如果excel中有一列存的是手机号码或者身份证号码&#xff0c;那么导入到sql中时&#xff0c;会把手机或者身份证当作数字格式对待&#xff0c;因而会以科学记数法的形式存在sqlserver表中&#xff0c;解决方式&#xff0c;先将excel文件另存为文本文件&#xff08;制表符&#…

Git的其他用法

2019独角兽企业重金招聘Python工程师标准>>> 目录&#xff1a; 减少【.git】文件夹的大小和文件数更换git for windows的文本编辑器修改已经提交的commit说明合并commit解决merge时出现的冲突回退一个merge获取某一commit的修改将低版本push到Github&#xff08;删掉…

UE中的几个极有用功能

1. 指定目录和文件类型批量查找目标字符串 示例&#xff1a;在H:\qtdemo目录&#xff08;含子目录&#xff09;中下的*.h和*.cpp中&#xff0c;查找“main”字符串 查找结果&#xff1a; 2. 在当前活动窗口中查找目标字符串&#xff08;Ctrl F&#xff09; &#xff08;1&…

Missing space before value for key ‘routes‘ key-spacing

vue项目报错&#xff1a; Missing space before value for key ‘routes’ key-spacing 解决办法&#xff1a; 字母new前面应该是1个空格&#xff0c;我写了两个空格&#xff0c;删去一个空格即可

Oracle Mutex 机制 说明

之前也整理过一篇文章来说明Oracle Lock的&#xff0c;参考&#xff1a; 锁 死锁 阻塞 Latch 等待 详解 http://blog.csdn.net/tianlesoftware/archive/2010/08/19/5822674.aspx 在这篇文章里&#xff0c;提到了System Locks&#xff0c;它包含&#xff1a; &#xff08;1&…

POJ-1860-Currency Exchange

链接&#xff1a;https://vjudge.net/problem/POJ-1860 题意&#xff1a; 有N个点&#xff0c;支持货币兑换&#xff0c;从货币a->b手续费c&#xff0c;汇率r。 求能否换一圈使总净额增加。 思路&#xff1a; bellman-ford。 找一个正权回路。 代码&#xff1a; #include &l…

如果asp.net mvc中某个action被执行了两次,请检查是不是以下的原因

注释 <link rel"icon" href"#"> 这一句后试试转载于:https://www.cnblogs.com/lummon/p/4559185.html

vue常见错误汇总(自看)

解决办法汇总 eslint: Expected indentation of 2 spaces but found 4缩进报错 &#xff0c;所有缩进只能用两个空格 Newline required at end of file but not found需要在最后的后面再加一行!!! Missing space before value for key ‘name’在关键字“值”之前缺少空格 …

IOS的钥匙串,确保本地隐私数据的安全

* 苹果的"生态圈"&#xff0c;钥匙串访问&#xff0c;使用 AES 256 加密算法&#xff0c;能够保证用户密码的安全 * 钥匙串访问SDK&#xff0c;是苹果在 iOS 7.0.3 版本以后公布的 * 钥匙串访问的接口是纯 C 语言的&#xff0c;但是&#xff0c;网络上有框架把它封装…

【转】PendingIntent的总结

Intent和PendingIntent的关系&#xff0c;初学的时候很迷惑&#xff0c;用PendingIntent的时候&#xff0c;还会出现奇怪的问题&#xff0c;比如无法传递数据&#xff0c;无法更新数据&#xff0c;所以我集众家之长&#xff0c;加上我个人的一些实践&#xff0c;总结如下&#…

CentOS 7 安装 GlusterFS

目录 环境说明&#xff1a; 3台机器安装 GlusterFS 组成一个集群。 使用 docker volume plugin GlusterFS 服务器&#xff1a; 10.6.0.140 10.6.0.192 10.6.0.196转载于:https://www.cnblogs.com/MeiCheng/p/10274222.html

npm安装less报错 rollbackFailedOptional: verb npm-session

解决办法 在cmd中依次输入然后回车 &#xff08;1&#xff09; npm install -g cnpm --registryhttps://registry.npm.taobao.org &#xff08;2&#xff09; cnpm install less less-loader --save-dev

Online Judge上陪审团选人问题用Java实现的一个AC解

原问题位于&#xff1a;http://poj.org/problem?id1015 以下为问题描述的摘录&#xff1a; In Frobnia, a far-away country, the verdicts in court trials are determined by a jury consisting of members of the general public. Every time a trial is set to begin, a j…

【D3】transition API

摘要&#xff1a; 动画类API 一、API 使用 1. 1 d3.ease 1.2 d3.timer Start a custom animation timer, invoking the specified function repeatedly until it returns true. There is no way to cancel the timer after it starts, so make sure your timer function return…

微信小程序 - 富文本图片宽度自适应(正则)

引言&#xff1a;在微信小程序里&#xff0c;比如商品展示页面的商品详情会有图片展示&#xff0c;PC端设置的商品详情是PC端的宽度&#xff0c;所以在小程序里图片会显示不全&#xff0c;这时就应该做相应的处理&#xff0c;使小程序里图片显示正确 思路 把图片的宽度改为手机…

(1)01背包问题

#include <stdio.h> #define N 6 #define M 21 #define W 20 int dp[N][M]; int w[N] {0}; int v[N] {0}; void knapsack(){int i, j;int value1, value2;for(i 1; i < N; i){ // 前i件物品for(j 1; j < N; j){ // 背包剩余空间if(w[i] > j){ // 第i件物品太…

Web.Config文件配置之限制上传文件大小和时间

在邮件发送系统或者其他一些传送文件的网站中&#xff0c;用户传送文件的大小是有限制的&#xff0c;因为这样不但可以节省服务器的空间&#xff0c;还可以提高传送文件的速度。下面介绍如何在Web.Config文件中配置限制上传文件大小与时间。 在Web.Config文件中配置限制上传文件…

成为软件高手的几个忌讳

1&#xff09; 不会英语&#xff1a;CS源于美国&#xff0c;重量级的文档都是英文的。不会英语&#xff0c;那么你只能忍受拙劣的翻译和大延迟的文档&#xff08;翻译出来的文档几乎都是很久以前出版的东西&#xff09;。2&#xff09; 急于求成&#xff1a;什么都没学习就开始…

Linux下的redis的持久化,主从同步及哨兵

redis持久化 Redis是一种内存型数据库&#xff0c;一旦服务器进程退出&#xff0c;数据库的数据就会丢失&#xff0c; 为了解决这个问题&#xff0c;Redis提供了两种持久化的方案&#xff0c;将内存中的数据保存到磁盘中&#xff0c;避免数据的丢失。 RDB持久化 redis提供了RDB…

vue/require-v-for-key]Elements in iteration expect to have ‘v-bind:key‘ directives

报错内容&#xff1a;[vue/require-v-for-key]Elements in iteration expect to have v-bind:key directives.解决&#xff1a;加上v-bind:key"index"<p>主演:<!--显示电影主演&#xff0c;循环--><span v-for"(actor, index) in scope.row.act…

@Ignore_JUnit - Ignore Test

Ignore 用法很简单, 如果你的测试用例还没有准备好而不想被执行, 又不想删掉或注释掉, 可以使用 Ignore 标注来忽略测试。 方法一旦用 Ignore 注解了将不会被执行. 如果一个类用 Ignore 注解了 他下面的所有测试方法将不会被执行. 看个应用 Create a Class Create a java clas…

Redis5.0之Stream案例应用解读

2019独角兽企业重金招聘Python工程师标准>>> 非常高兴有机会和大家在这里交流Redis5.0之Stream应用。今天的分享更多的是一个抛砖引玉&#xff0c;欢迎大家提出更多关于Redis的思考。 首先&#xff0c;我们来个假设&#xff0c;这里有个杯子&#xff0c;这个杯子是去…

往阿里云服务器上安装Mysql

在安装完Mysql后要进行登录&#xff0c;这时候显示让你输密码&#xff0c; 我点击键盘发现怎么没有密码出现&#xff0c; 然后我就把Mysql卸载了重新装的&#xff0c;折腾了2个多小时&#xff0c; 最后&#xff0c;我突然想试试直接输密码然后就回车&#xff0c; 发现成功进入数…