[UWP]领会IValueConverter

[源码下载]

1. 前言

IValueConverter是用于数据绑定的兵不血刃的枪炮,它用来Value在Binding
Source和Binding
Target之间的更换。本文将介绍IValueConverter的用法及部分常用的兑现。

背水第一回大战 Windows 10 (八三) – 用户和账号: 数据账号的丰盛和管理, OAuth 贰.0
验证

二. 为啥要动用IValueConverter

借使有如下的类TestResult:

public class TestResult
{
    public bool Passed { get; set; }

}

UI要求经过Passed那几个天性决定展现结果的文字颜色为浅蓝绿或紫肉桂色,1般初学者最常见的做法是修改TestResult类,添加二个和Passed相关的属性:

public class TestResult
{
    public bool Passed { get; set; }

    public Brush TestResultBrush
    {
        get
        {

            if (Passed)
                return new SolidColorBrush(Colors.Red);
            else
                return new SolidColorBrush(Colors.Green);
        }
    }
}

下一场在XAML上绑定到那个天性:

<TextBlock  Text="Score : 60" Foreground="{Binding TestResultBrush}"/>

另1种做法是直接才Code Behind为TextBlock更改Foreground:

var testResult = DataContext as TestResult;
if (testResult != null)
{
    if (testResult.Passed)
        ResultElement.Foreground = new SolidColorBrush(Colors.Red);
    else
        ResultElement.Foreground = new SolidColorBrush(Colors.Green);
}

二种做法都不够优雅,能够建议一大堆难点:破坏了TestResult的布局,违反了开放封闭原则,令UI和数据太过耦合,太多Hard
Code等。

那种情景壹般都足以行使IValueConverter处理。在Binding中,IValueConverter能够用于数据显现前将它转换来新的目的值,实现IValueConverter须要履行以下步骤:

  1. 创造1个落到实处了IValueConverter接口的类类;
  2. 实现public object Convert(object value, Type targetType, object parameter, string language)办法,该措施将数据转换为新对象值;
  3. 实现public object ConvertBack(object value, Type targetType, object parameter, string language),该办法执行反向转换,唯有接纳双向绑定才必要落成这些办法。

在这几个例子里,IValueConverter的目标是将bool类型的Passed转换到Brush,完成如下:

public class BoolToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value is bool passed)
            return new SolidColorBrush(passed ? Colors.Green : Colors.Red);

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

在XAML中应用那一个Convnerter必要先将它定义为Resource,然后Binding中钦命Converter到这些已定义的Resource:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.Resources>
        <local:BoolToBrushConverter x:Key="BoolToBrushConverter"/>
    </Grid.Resources>
    <TextBlock  Text="Score : 60" Foreground="{Binding Passed,Converter={StaticResource BoolToBrushConverter}}"/>
</Grid>

作者:webabcd

3. BoolToValueConverter

在XAML漫长的历史里,IValueConverter也落地了各个意料之外的技艺,在那之中最常用的是BoolToValueConverter。

public class BoolToValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null || (bool) value == false)
            return DependencyProperty.UnsetValue;

        return parameter;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return Equals(value, parameter);
    }
}

BoolToValueConverter灵活使用了Binding中ConverterParameterFallbackValue八个参数,平常用于缓解IValueConverter中HardCode的难点。在Binding中,FallbackValue指明了如若Binding无法再次来到任何值时行使的值,在IValueConverter中回到DependencyProperty.UnsetValue即告诉Binding要使用FallbackValue的值。

利用BoolToValueConverter消除了上述例子的Hard
Code的标题,在XAML中选拔如下:

<Grid>
    <Grid.Resources>
        <local:BoolToValueConverter x:Key="BoolToValueConverter"/>
    </Grid.Resources>
    <TextBlock Text="Score : 60" Foreground="{Binding Passed,Converter={StaticResource BoolToValueConverter},ConverterParameter=Green,FallbackValue=Red}"/>
</Grid>

介绍
背水世界一战 Windows 拾 之 用户和账号

4. BoolToObjectConverter

亟待留意的是地点XAML中格林和Red都只是字符串,它们最终能被分析成SolidColorBrush是由于TypeConveter的补助,也正是说上述XAML语法只好用来TypeConverter协理的数据类型,而且那种写法依旧太过HardCode。假诺要辅助复杂类型或然对应本地化等难点,能够将ConverterParameter和FallbackValue绑定到StaticResource

<Grid.Resources>
    <SolidColorBrush Color="Green" x:Key="PassedBrush"/>
    <SolidColorBrush Color="Red" x:Key="FailedBrush"/>
    <local:BoolToValueConverter x:Key="BoolToValueConverter"/>
</Grid.Resources>
<TextBlock Text="Score : 60" Foreground="{Binding Passed,Converter={StaticResource BoolToValueConverter},ConverterParameter={StaticResource PassedBrush},FallbackValue={StaticResource FailedBrush}}"/>

虽说看上去是很灵活,但只要有雅量回来同样值的BoolToValueConverter将会使XAML爆发大批量冗余。UWP
Community
Toolkit
提供了部分常用的IValueConverter完成,当中最常用的是BoolToObjectConverter。BoolToObjectConverter和BoolToValueConverter成效类似,但它提供了public object TrueValue { get; set; }public object FalseValue { get; set; }几个本性,而且那三个特性是依靠属性,能够应用绑定为其赋值。使用如下:

<Grid.Resources>
    <SolidColorBrush Color="Green" x:Key="PassedBrush"/>
    <SolidColorBrush Color="Red" x:Key="FailedBrush"/>
    <converters:BoolToObjectConverter x:Key="BoolToObjectConverter" TrueValue="{StaticResource PassedBrush}" FalseValue="{StaticResource FailedBrush}"/>
</Grid.Resources>
<TextBlock Text="Score : 60" Foreground="{Binding Passed,Converter={StaticResource BoolToObjectConverter}}"/>
  • 数据账号的丰盛和管
  • OAuth 2.0 验证

5. BoolToVisibilityConverter

UWP Community
Toolkit中提供了另三个常用的Converter:BoolToVisibilityConverter。那么些Converter只是不难地继续了BoolToObjectConverter,并且为TrueValue和FalseValue设置了暗许值:

public BoolToVisibilityConverter()
{
    TrueValue = Visibility.Visible;
    FalseValue = Visibility.Collapsed;
}

BoolToVisibilityConverter就算不难,但实在好用。可是从1607事后就不须求这么些Converter了,微软是这样说的:

从 Windows 10 版本 1607 初叶,XAML 框架向 Visibility
转换器提供放置布尔值。 转换器将 true 映射到 Visible 枚举值并将 false
映射到 Collapsed,以便你能够将 Visibility
属性绑定到布尔值,而无需创制转换器。
若要选取内置转换器,你的选择的最低指标 SDK 版本必须为 143玖三或更高版本。

但偶尔反而供给True对应Collapsed,于是今后是另2个常用Converter –
管理,BoolNegationConverter登上历史舞台的时候了:

<StackPanel >
    <StackPanel.Resources>
        <converters:BoolNegationConverter x:Key="BoolNegationConverter" />
    </StackPanel.Resources>
    <TextBlock Text="Passed" Foreground="Green" Visibility="{Binding Passed}"/>
    <TextBlock Text="Failed" Foreground="Red" Visibility="{Binding Passed,Converter={StaticResource BoolNegationConverter}}"/>
</StackPanel>

示例
1、演示数据账号的增进和管制
UserAndAccount/DataAccount.xaml

6. StringFormatConverter

UWP的Binding缺乏了StringFormat,那对Binding发生了非常的大影响,为弥补那一个毛病,能够采用UWP
Community
Toolkit中的StringFormatConverter。它的代码也尤其简练(其实这才是ConverterParameter的科学用法):

public object Convert(object value, Type targetType, object parameter, string language)
{
    if (value == null)
    {
        return null;
    }

    if (parameter == null)
    {
        return value;
    }

    return string.Format((string)parameter, value);
}

在XAML中行使如下:

<TextBlock Text="{Binding DoubleValue,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0:N2}'}"/>
<TextBlock Text="{Binding DoubleValue,Converter={StaticResource StringFormatConverter},ConverterParameter='There are {0:N0} Items'}"/>

结果如下:

管理 1

除去弥补StringFormat的功能,StringFormatConverter还有其余的利用场景。

** TestModel.CS **

public IEnumerable<ClickMode> ClickModes => new List<ClickMode> { ClickMode.Hover, ClickMode.Press, ClickMode.Release };

<ListBox ItemsSource="{Binding ClickModes}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding }" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
<ListBox ItemsSource="{Binding ClickModes}"/>

在WPF中,以上XAML都足以健康呈现,而在UWP中,以上XAML彰显如下:

管理 2

那种意况能够应用StringFormatConverter显示枚举的称号:

<ListBox ItemsSource="{Binding ClickModes}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource StringFormatConverter}}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

管理 3

能够说对UWP来说StringFormatConverter1二分必要。

<Page
    x:Class="Windows10.UserAndAccount.DataAccount"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.UserAndAccount"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <TextBlock Name="lblMsg" Margin="5" />

            <Button Name="buttonAdd" Content="新增一个数据账号" Margin="5" Click="buttonAdd_Click" />

        </StackPanel>
    </Grid>
</Page>

7. language参数

public object Convert(object value, Type targetType, object parameter, string language)主意中的参数language平日用于本地化,例如能够成立3个DateTimeValueConverter:

public class DateTimeValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value is DateTime dateTime)
        {
            var culture = new CultureInfo(language);
            return dateTime.ToString(culture.DateTimeFormat);
        }
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

<TextBlock Text="{Binding Date,Converter={StaticResource DateTimeValueConverter},ConverterLanguage=en-US}"/>
<TextBlock Text="{Binding Date,Converter={StaticResource DateTimeValueConverter},ConverterLanguage=zh-CN}"/>

结果如下:

管理 4

UserAndAccount/DataAccount.xaml.cs

8. targetType参数

targetType参数指转换后的靶子项目,使用那几个参数能够兑现二个粗略的Value
Converter:

public class ValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return System.Convert.ChangeType(value, targetType);
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

固然如此代码简单,但它能够化解许多题材,例如
了解TypeConverter
那篇文章里关系的不可能在XAML中央银行使decimal的难题。IValueConverter要起作用信赖于BindingSource,而在XAML中尽管很多东西都得以用来做BindingSource,例如用成分协调的Tag:

<local:MyContentControl Tag="10.01" Amount="{Binding Converter={StaticResource ValueConverter},Path=Tag,RelativeSource={RelativeSource Mode=Self}}"/>

照旧Resources中的字符串:

<Grid.Resources>
    <x:String x:Key="DecimalString">10.01</x:String>
</Grid.Resources>
<local:MyContentControl Amount="{Binding Source={StaticResource DecimalString},Converter={StaticResource ValueConverter}}"/>

或然更进一步写3个字符串的包装类:

public class StringWrapper
{
    public string this[string key]
    {
        get
        {
            return key;
        }
    }
}

<local:MyContentControl Amount="{Binding [10.01],Source={StaticResource StringWrapper},Converter={StaticResource ValueConverter}}"/>
/*
 * 演示数据账号的添加和管理
 * 
 * UserDataAccountManager - 数据账号管理器
 *     ShowAddAccountAsync() - 弹出账号添加界面
 *     ShowAccountSettingsAsync() - 弹出账号管理界面
 *     RequestStoreAsync() - 返回当前用户的数据账号存储区域
 *     GetForUser() - 返回指定用户的数据账号存储区域(通过返回的 UserDataAccountManagerForUser 对象的 RequestStoreAsync() 方法)
 *     
 * UserDataAccountStore - 数据账号存储区域
 *     FindAccountsAsync() - 返回所有的数据账号
 *     GetAccountAsync() - 返回指定的数据账号
 *     
 * UserDataAccount - 数据账号
 *     UserDisplayName - 用户名
 *     Id - 数据账号在本地设备上的唯一标识
 *     SaveAsync() - 保存
 *     DeleteAsync() - 删除
 *     ... 还有很多其他属性和方法
 *     
 *     
 * 注:根据使用的功能需要在 Package.appxmanifest 做相关配置
 * 1、用到 Windows.System.User 的话需要配置 <Capability Name="userAccountInformation" />
 * 2、还可能需要 <Capability Name="appointments" />, <Capability Name="contacts" />
 */

using System;
using System.Linq;
using System.Collections.Generic;
using Windows.ApplicationModel.UserDataAccounts;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Windows10.UserAndAccount
{
    public sealed partial class DataAccount : Page
    {
        public DataAccount()
        {
            this.InitializeComponent();
        }

        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            // 获取当前用户下的全部数据账号
            UserDataAccountStore store = await UserDataAccountManager.RequestStoreAsync(UserDataAccountStoreAccessType.AllAccountsReadOnly);
            IReadOnlyList<UserDataAccount> accounts = await store.FindAccountsAsync();
            lblMsg.Text += string.Join(",", accounts.Select(p => p.UserDisplayName));
            lblMsg.Text += Environment.NewLine;
        }

        private async void buttonAdd_Click(object sender, RoutedEventArgs e)
        {
            // 弹出账号添加界面,如果添加成功会返回新建的数据账号的在本地设备上的唯一标识
            string userDataAccountId = await UserDataAccountManager.ShowAddAccountAsync(UserDataAccountContentKinds.Email | UserDataAccountContentKinds.Appointment | UserDataAccountContentKinds.Contact);

            if (string.IsNullOrEmpty(userDataAccountId))
            {
                lblMsg.Text += "用户取消了或添加账号失败";
                lblMsg.Text += Environment.NewLine;
            }
            else
            {
                UserDataAccountStore store = await UserDataAccountManager.RequestStoreAsync(UserDataAccountStoreAccessType.AllAccountsReadOnly);
                if (store != null)
                {
                    // 通过数据账号在本地设备上的唯一标识来获取 UserDataAccount 对象
                    UserDataAccount account = await store.GetAccountAsync(userDataAccountId);
                    lblMsg.Text += "新增的数据账号:" + account.UserDisplayName;
                }
            }
        }
    }
}

九. 运用IValueConverter的别样经验

二、演示如何开发二个基于 OAuth 二.0 验证的客户端 
UserAndAccount/OAuth20.xaml

玖.一 统一保管IValueConverter

由于多数IValueConverter行为是固定的,经常本人都会把常用的IValueConverter放到3个Converters.xaml,然后在App.xaml中年集合营源字典,那样不用重新写创建Converter的xaml,也幸免了双重创设Converter的财富消耗:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Converters.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>
<Page
    x:Class="Windows10.UserAndAccount.OAuth20"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.UserAndAccount"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <Button Name="buttonWeibo" Content="登录新浪微博,并返回登录用户好友最新发布的微博" Margin="5" Click="buttonWeibo_Click" />

            <TextBlock Name="lblMsg" TextWrapping="Wrap" Margin="5" />

        </StackPanel>
    </Grid>
</Page>

9.2 格式化

管理 5

Binding最让人诟病的欠缺正是它的语法太长太长太长,例如以上八个TextBlock,在IDE中很难断定那它们有哪些两样。很多时候我都会把XAML的格式化设置成“将各类属性分行放置”,如下图:
管理 6

如此那般地点三个TextBlock的XAML就清楚许多了:

管理 7

然则那样设置也并不全是好处,怎么设置具体依旧看个人喜欢和显示器尺寸。

UserAndAccount/OAuth20.xaml.cs

10. 结语

虽说IValueConverter的篇章已经重重了,但要么不时见到乱来的IValueConverter达成,而且UWP的IValueConverter有1些改成,所以照旧写了那篇小说。

自个儿很想写1些常用的,恐怕简单用错的基础知识,但连IValueConverter都无心就写得如此长了,实在没勇气写Binding的定义,何况关于Binding
已经有好多很实用的稿子。

自笔者万分明白文章写得太长就会被“保存到Pocket”,作者也想每篇文章都能在三5分钟内看完,但偏偏越基础的概念就越能写得长,而且写得不难些又会被移出和讯首页,很难把握标准。

下一篇小说会尽力而为写短1些。

/*
 * 演示如何开发一个基于 OAuth 2.0 验证的客户端 
 * 关于 OAuth 2.0 协议请参见:http://tools.ietf.org/html/draft-ietf-oauth-v2-20
 * 
 * WebAuthenticationBroker - 用于 OAuth 2.0 验证的第一步,可以将第三方 UI 无缝整合进 app
 *     AuthenticateAsync(WebAuthenticationOptions options, Uri requestUri, Uri callbackUri) - 请求 authorization code,返回一个 WebAuthenticationResult 类型的数据
 *
 * WebAuthenticationResult - 请求 authorization code(OAuth 2.0 验证的第一步)的结果
 *     ResponseData - 响应的数据         
 *     ResponseStatus - 响应的状态
 * 
 * 
 * 注:本例以微博开放平台为例
 */

using System;
using System.Net.Http;
using System.Text.RegularExpressions;
using Windows.Data.Json;
using Windows.Security.Authentication.Web;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Windows10.UserAndAccount
{
    public sealed partial class OAuth20 : Page
    {
        public OAuth20()
        {
            this.InitializeComponent();
        }

        private async void buttonWeibo_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var appKey = "39261162";
                var appSecret = "652ec0b02f814d514fc288f3eab2efda";
                var callbackUrl = "http://webabcd.cnblogs.com"; // 在新浪微博开放平台设置的回调页

                var requestAuthorizationCode_url =
                    string.Format("https://api.weibo.com/oauth2/authorize?client_id={0}&response_type=code&redirect_uri={1}",
                    appKey,
                    callbackUrl);

                // 第一步:request authorization code
                WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
                    WebAuthenticationOptions.None,
                    new Uri(requestAuthorizationCode_url),
                    new Uri(callbackUrl));

                // 第一步的结果
                lblMsg.Text = WebAuthenticationResult.ResponseStatus.ToString() + Environment.NewLine;

                if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
                {
                    // 从第一步返回的数据中获取 authorization code
                    var authorizationCode = QueryString(WebAuthenticationResult.ResponseData, "code");
                    lblMsg.Text += "authorizationCode: " + authorizationCode + Environment.NewLine;

                    var requestAccessToken_url =
                        string.Format("https://api.weibo.com/oauth2/access_token?client_id={0}&client_secret={1}&grant_type=authorization_code&redirect_uri={2}&code={3}",
                        appKey,
                        appSecret,
                        callbackUrl,
                        authorizationCode);

                    // 第二步:request access token
                    HttpClient client = new HttpClient();
                    var response = await client.PostAsync(new Uri(requestAccessToken_url), null);

                    // 第二步的结果:获取其中的 access token
                    var jsonString = await response.Content.ReadAsStringAsync();
                    JsonObject jsonObject = JsonObject.Parse(jsonString);
                    var accessToken = jsonObject["access_token"].GetString();
                    lblMsg.Text += "accessToken: " + accessToken + Environment.NewLine;

                    var requestProtectedResource_url =
                        string.Format("https://api.weibo.com/2/statuses/friends_timeline.json?access_token={0}",
                        accessToken);

                    // 第三步:request protected resource,获取需要的数据(本例为获取登录用户好友最新发布的微博)
                    var result = await client.GetStringAsync(new Uri(requestProtectedResource_url)); // 由于本 app 没有提交微博开放平台审核,所以如果使用的账号没有添加到微博开放平台的测试账号中的话,是会出现异常的
                    lblMsg.Text += "result: " + result;
                }
            }
            catch (Exception ex)
            {
                lblMsg.Text += Environment.NewLine;
                lblMsg.Text += ex.ToString();
            }
        }

        /// <summary>
        /// 模拟 QueryString 的实现
        /// </summary>
        /// <param name="queryString">query 字符串</param>
        /// <param name="key">key</param>
        private string QueryString(string queryString, string key)
        {
            return Regex.Match(queryString, string.Format(@"(?<=(\&|\?|^)({0})\=).*?(?=\&|$)", key), RegexOptions.IgnoreCase).Value;
        }
    }
}

/*
 * OAuth 2.0 的 Protocol Flow
     +--------+                               +---------------+
     |        |--(A)- Authorization Request ->|   Resource    |
     |        |                               |     Owner     |
     |        |<-(B)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(C)-- Authorization Grant -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+
*/

11. 参考

IValueConverter
Interface

Binding
Class

深切摸底多少绑定
Converters – UWP Community Toolkit _ Microsoft
Docs

OK
[源码下载]

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注