功能简介
在数据绑定的时候,我们有时候会遇到不能直接绑定的情况,如Checkbox的IsChecked属性不能直接绑定一个布尔值,Image的Source属性不能直接绑定到一个string上,这时候就需要添加转换器完成数据绑定
示例项目将编写两个转换器,使Checkbox的IsChecked属性通过转换器绑定一个布尔值上,Image的Source属性通过转换器绑定到一个string上;
示例项目将使用到部分文件存储和双向绑定的内容,但这只是为了演示转换器的功能,本文的重点在于CheckBoxConverter类和ImageConverter的实现,如果想要了解文件存储和双向绑定的内容,可以参考另一篇博客:UWP文件管理和UWP数据绑定——双向绑定
实现效果
- 启动程序时:Item的属性值通过转换器给Image和Checkbox赋值
- 点击按钮更改图片,Item的Source属性变化,Image的Source属性值实时更新
实现过程
添加一个类CheckBoxConverter
该类将继承接口IValueConverter,同时实现接口的两个函数
/*
* value为需要转化的值
* targetType为要转化成的值的类型
* parameter为转化时传递的参数,
* 根据value的值,返回一个targetType类型的值
*/
public object Convert(object value, Type targetType, object parameter, string language){
......
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
......
}
示例代码中的CheckBoxConverter的功能是将布尔值转化为Checkbox的IsChecked属性值,具体代码如下:
//CheckBoxConverter.cs
namespace DataBindingWithConverter
{
/*
* 转化器,将布尔值转化为Checkbox的IsChecked属性(System.Nullable`1[System.Boolean])
*/
public class CheckBoxConverter : IValueConverter
{
/*
* value为Item的Ischecked 属性,布尔值
* targetType为Checkbox的Ischecked 属性的类型
* parameter为转化时传递的参数,这里为空
*/
public object Convert(object value, Type targetType, object parameter, string language)
{
/*
* bool? 表示isChecked可以取null
* value as bool? 将value转化为布尔值然后返回,如果不能转化,则返回null
*/
bool? isChecked = value as bool?;
/*
* 实际上这里返回的并不是targetType类型的值,而是布尔值
* 但是在代码中返回值为Object,布尔值将被强制转化
* 注意:在类型不能强制转化的情况下,应该根据targetType的类型返回返回需要的值
*/
if (isChecked == null || isChecked == false)
{
return false;
}
else
{
return true;
}
}
/*
* 因为是单向绑定,所有并不需要编写ConvertBack函数
* 如果使用了双向绑定,可以参考上面函数的写法添加ConvertBack函数的代码
*/
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
需要注意的地方:
- 继承接口IValueConverter
- 根据value和targetType定义Convert函数
同理添加一个类ImageConverter
//ImageConverter.cs
namespace DataBindingWithConverter
{
class ImageConverter : IValueConverter
{
//string转ImageSource(Image的Source属性)
public object Convert(object value, Type targetType, object parameter, string language)
{
string source = value as string;
ImageSource imageSource = new BitmapImage(new Uri(source));
return imageSource;
}
//Mode = Oneway,所以不用管ConvertBack的内容
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
新建类Item.cs
//Item.cs
namespace DataBindingWithConverter
{
/*
* Item是我们自定义的一个类型,包含三个属性,对应三个控件的属性
* Source --> Image的Source
* Ischecked --> Checkbox的checked
* 这里继承了接口INotifyPropertyChanged,当Item某一属性值发生更改时,发出通知
* 导致控件的属性也实时更改
* 这里主要是为了测试转化器才这么使用的,可以暂时不用管INotifyPropertyChanged的作用
*/
public class Item : INotifyPropertyChanged
{
private string _source;
private bool _ischecked;
public string Source
{
get => _source;
set { _source = value; OnPropertyChanged("Source"); }
}
public bool Ischecked
{
get => _ischecked;
set { _ischecked = value; OnPropertyChanged("Ischecked"); }
}
public Item(string source,bool ischecked)
{
this._source = source;
this._ischecked = ischecked;
}
//显示实现接口,实现数据绑定动态更新
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = "")
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
在MainPage.xaml.cs中添加代码
//MainPage.xaml.cs
namespace DataBindingWithConverter
{
public sealed partial class MainPage : Page
{
public Item MyItem;
//LocalFolder文件存储
private string prefix = "ms-appdata:///local/";
public MainPage()
{
MyItem = new Item("ms-appx:///Assets/flash.jpg", true);
this.InitializeComponent();
}
private async void ChangeImage(object sender, RoutedEventArgs e)
{
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.FileTypeFilter.Add(".jpg");
StorageFile file = await openPicker.PickSingleFileAsync();
//LocalFolder文件存储
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile fileCopy = await file.CopyAsync(localFolder, file.Name, NameCollisionOption.ReplaceExisting);
//设置MyItem的Source,此时将使用转化器转化为Image的Source值
MyItem.Source = prefix + file.Name;
}
}
}
需要注意的地方:
- 选择图片后需要把图片保存在LocalFolder,这里主要是因为权限问题
- 将MyItem的Source值实时更新为新的地址值
在MainPage.xaml中添加代码
首先,需要在Grid的上面添加代码:
<!--
将CheckboxConverter类添加到资源,因为CheckboxConverter和MainPage的命名空间一样,所有使用local
x:key可以设置为任何你喜欢的值,下面将通过这个值指向CheckboxConverter
-->
<Page.Resources>
<local:CheckBoxConverter x:Key="ConvertCheck"></local:CheckBoxConverter>
<local:ImageConverter x:Key="ConvertImage"></local:ImageConverter>
</Page.Resources>
然后在Grid中添加控件并绑定,代码如下:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<Image Source="{x:Bind MyItem.Source,Converter={StaticResource ConvertImage},Mode=OneWay}" ></Image>
<CheckBox IsChecked="{x:Bind MyItem.Ischecked,Converter={StaticResource ConvertCheck},Mode=OneWay}" Content="Whether the CheckBox is checked is depend on the Item."></CheckBox>
<Button Content="ChangePicture" Click="ChangeImage" Width="300"></Button>
</StackPanel>
</Grid>
需要注意的地方:
一开始资源文件的声明
CheckBox的IsChecked属性的绑定,Image的Source属性的绑定
总结
总的说来,转化器 + 数据绑定的大致过程就是:
- 根据需要转化的值和目标类型编写转化器
- 在xaml.cs中声明资源文件
- 绑定属性是添加转化器
其实,选择图片后还可以按下面的设置更新Image控件:
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.FileTypeFilter.Add(".jpg");
file = await openPicker.PickSingleFileAsync();
//将图片赋给右侧的Image
if (file != null)
{
BitmapImage bitmap = new BitmapImage();
using (var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
bitmap.SetSource(stream);
}
//这里的MyImage为Image的x:Name值
MyImage.Source = bitmap;
}
但是为什么不这么做呢?主要是因为两点,一,本文主要是为了演示如何使用转换器,所以更新Item.Source的值然后再使用转换器更新Image;二,在之后使用了MvvmLight之后,你会发现将视图和功能分离的重要性ヽミ ´∀`ミノ<
Tips
可能你和我一样不知道Convert和ConvertBack的目标类型是什么,总是搞混,这时候其实我们可以通过Debug.WriteLine()方法打印出目标类型
using System.Diagnostics;
Debug.WriteLine(targetType);
FYI
项目下载之后记住把Debug模式从ARM换成X86或X64(根据你自己的机型选择),之前一开始学习的时候不知道这一点,从网上下载下来的项目都运行不了,纠结的一逼(╥╯^╰╥)