WPF桌面端开发3-转换器Converter

简单转换器

我们可以写一下常用的用于特定转换的转换器

Boolean => Visibility

系统自带的正向转换器

1
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />

反向转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows;

public class BooleanToVisibilityInverseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isVisible = value != null && (bool)value;
return isVisible ? Visibility.Collapsed : Visibility.Visible;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
Visibility? visibility = (Visibility?)value;
return visibility == Visibility.Collapsed;
}
}

添加引用

1
<mConverter:BooleanToVisibilityInverseConverter x:Key="BooleanToVisibilityInverseConverter" />

通用值转换器

经常我们在绑定数据的时候需要的数据类型和原数据不同,这时候我们就需要对数据进行转换

工具类

C#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
namespace Z.Converters
{
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;

public class ZCommonConverter : IValueConverter
{
/// <summary>
/// 正向键值对字典
/// </summary>
private Dictionary<string, string> Alias { get; set; }

/// <summary>
/// 反向键值对字典
/// </summary>
private Dictionary<string, string> BackAlias { get; set; }

private string _aliasStrTemp = "";

/// <summary>
/// 解析转换规则
/// </summary>
/// <param name="aliasStr">
/// 规则字符串
/// </param>
private void ParseAliasByStr(string aliasStr)
{
if (_aliasStrTemp == aliasStr)
{
return;
}

_aliasStrTemp = aliasStr;
Alias = new Dictionary<string, string>();
BackAlias = new Dictionary<string, string>();

string content = aliasStr;

Alias = new Dictionary<string, string>();
string[] arr1 = content.Split('|');
foreach (string item in arr1)
{
string[] arr2 = item.Split(':');
string key = arr2[0];
key = key switch
{
"true" => "True", "false" => "False", _ => key
};
string value = arr2[1];
if (key != "other")
{
if (!BackAlias.ContainsKey(value))
{
BackAlias.Add(value, key);
}
}
if (!Alias.ContainsKey(key))
{
Alias.Add(key, value);
}
}
}

private object ConvertCommon(object value, Type targetType, object parameter, bool isBack)
{
if (value == null || string.IsNullOrEmpty(parameter.ToString()))
{
return null;
}

object ret = value;//如果没有匹配返回传入的值

ParseAliasByStr(parameter.ToString());
Dictionary<string, string> alias = isBack ? BackAlias : Alias;
//绑定的值
string bindingValue = value.ToString();

if (alias.ContainsKey(bindingValue))
{
ret = StringToTargetType(alias[bindingValue], targetType);
}
else if (alias.ContainsKey("other"))
{
ret = StringToTargetType(alias["other"], targetType);
}
else if (alias.ContainsKey("else"))
{
ret = StringToTargetType(alias["else"], targetType);
}

return ret;
}

/// <summary>
/// 字符串转换成目标类型,如需添加一个目标类型只需在该方法中添加一个类型判断之后转换
/// </summary>
/// <param name="strValue">
/// </param>
/// <param name="targetType">
/// </param>
/// <returns>
/// </returns>
private object StringToTargetType(string strValue, Type targetType)
{
object ret = null;
//目标类型 string
if (targetType == typeof(string) || targetType == typeof(char))
{
ret = strValue;
}
//目标类型 char
if (targetType == typeof(char))
{
if (strValue.Length == 1)
{
ret = strValue;
}
}
//目标类型 int
if (targetType == typeof(int))
{
ret = int.TryParse(strValue, out int temp) ? temp : 0;
}
//目标类型 double
if (targetType == typeof(double))
{
if (double.TryParse(strValue, out double temp))
{
ret = temp;
}
else
{
ret = 0;
}
}
//目标类型 float
if (targetType == typeof(float))
{
if (float.TryParse(strValue, out float temp))
{
ret = temp;
}
else
{
ret = 0;
}
}
//目标类型 decimal
if (targetType == typeof(decimal))
{
ret = decimal.TryParse(strValue, out decimal temp) ? temp : 0;
}
//目标类型 bool? bool
if (targetType == typeof(bool?) || targetType == typeof(bool))
{
ret = bool.TryParse(strValue, out bool temp) && temp;
}

//目标类型 Visibility
if (targetType == typeof(Visibility))
{
switch (strValue.ToLower())
{
case "collapsed":
case "none":
ret = Visibility.Collapsed;
break;
case "hidden":
case "hide":
ret = Visibility.Hidden;
break;
case "visible":
case "show":
ret = Visibility.Visible;
break;
default:
ret = Visibility.Visible;
break;
}
}
if (targetType == typeof(Brush))
{
// 通过RGB字符串创建红色
Color myColor = (Color)ColorConverter.ConvertFromString(
strValue
)!;

// 创建SolidColorBrush对象
SolidColorBrush myBrush = new SolidColorBrush(
myColor
);
ret = myBrush;
}
if (targetType == typeof(Thickness))
{
return new Thickness(double.Parse(strValue));
}
return ret;
}

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ConvertCommon(value, targetType, parameter, false);
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ConvertCommon(value, targetType, parameter, true);
}
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<Window xmlns:converter="clr-namespace:Z.Converter">
<Window.Resources>
<converter:ZCommonConverter x:Key="ZCommonConverter" />
</Window.Resources>
<Border
Width="80"
Height="120"
BorderBrush="#CCCCCC"
BorderThickness="1"
CornerRadius="2,2,2,2"
Visibility="{Binding Path=ISChecked, Converter={StaticResource ZCommonConverter}, ConverterParameter='true:Visible|false:Hidden'}" />

</Window>

颜色

背景

1
Background="{Binding Path=KYFile, Converter={StaticResource ZCommonConverter}, ConverterParameter='0:#666666|1:#019266|2:#FF001E'}"

前景色

1
Foreground="{Binding Path=KYFile, Converter={StaticResource ZCommonConverter}, ConverterParameter='0:#666666|1:#019266|2:#FF001E'}"

布尔型=>颜色

1
Background="{Binding Path=Selected, Converter={StaticResource ZCommonConverter}, ConverterParameter='true:#339DFF|other:#ffffff'}"

边框

边框颜色

1
BorderBrush="{Binding Path=Rank, Converter={StaticResource ZCommonConverter}, ConverterParameter='1:#FFA639|2:#1584FC|3:#FF7444|other:#EEEEEE'}"

边框粗细

1
BorderThickness="{Binding HideOuter, Converter={StaticResource ZCommonConverter}, ConverterParameter='true:1|false:0'}"

文字

1
Text="{Binding Path=KYFile, Converter={StaticResource ZCommonConverter}, ConverterParameter='0:未检测|1:正常|2:异常'}"

显示 Visibility

布尔型

1
2
Visibility="{Binding Path=Selected, Converter={StaticResource ZCommonConverter}, ConverterParameter='true:visible|other:collapsed'}"
Visibility="{Binding Path=Selected, Converter={StaticResource ZCommonConverter}, ConverterParameter='true:collapsed|other:visible'}"

数字型

1
Visibility="{Binding Path=selectIndex, Converter={StaticResource ZCommonConverter}, ConverterParameter='1:visible|other:hidden'}"

不同状态切换

1
2
Visibility="{Binding Path=selectIndex, Converter={StaticResource ZCommonConverter}, ConverterParameter='1:collapsed|other:visible'}"
Visibility="{Binding Path=selectIndex, Converter={StaticResource ZCommonConverter}, ConverterParameter='1:visible|other:collapsed'}"

图片加载转换器

主要解决加载本地图片内存不释放的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System;
using System.Globalization;
using System.IO;
using System.Windows.Data;
using System.Windows.Media.Imaging;

namespace ZConverter
{
public class StringToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string path = (string)value;
if (!string.IsNullOrEmpty(path))
{
return GetImage(path);
}
return null;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}

private BitmapImage GetImage(string imagePath)
{
BitmapImage bi = new BitmapImage();
if (File.Exists(imagePath))
{
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
using (Stream ms = new MemoryStream(File.ReadAllBytes(imagePath)))
{
bi.StreamSource = ms;
bi.EndInit();
bi.Freeze();
}
}
return bi;
}
}
}

引用

1
<zConverter:StringToImageSourceConverter x:Key="StringToImageSourceConverter" />

使用

1
<Image Source="{Binding Path=Pic, Converter={StaticResource StringToImageSourceConverter}}" Stretch="Fill" />

多值转换器

假如有这么一个场景,我们的组件要根据一个百分比根据父组件设置自身的宽度,使用单值绑定的时候是无法实现的,ConverterParameter中是不能绑定值的。

我们就只能使用多值绑定来实现,注意绑定的时候值的顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
<Grid>
<Grid Background="DodgerBlue"
HorizontalAlignment="Left">
<Grid.Width>
<MultiBinding Converter="{StaticResource PercentageConverter}">
<Binding
RelativeSource="{RelativeSource AncestorType={x:Type Grid}}"
Path="ActualWidth" />
<Binding Path="Percentage" />
</MultiBinding>
</Grid.Width>
</Grid>
</Grid>

转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace Z.Converters
{
using System;
using System.Globalization;
using System.Windows.Data;

public class PercentageConverter : IMultiValueConverter
{
public object Convert
(
object[] values,
Type targetType,
object parameter,
CultureInfo culture
)
{
if (values is { Length: 2 } && values[0] is double && values[1] is double)
{
double parentWidth = (double)values[0];
double percentage = (double)values[1];
return parentWidth * percentage;
}
return Binding.DoNothing;
}

public object[] ConvertBack
(
object value,
Type[] targetTypes,
object parameter,
CultureInfo culture
)
{
throw new NotImplementedException();
}
}
}

引用

1
<zcv:PercentageConverter x:Key="PercentageConverter" />