WPF Converter JSON 字符串到多个文本框
WPF Converter JSON string to multiple textboxes
由于工作时间有限,我已经针对这个问题做了一个解决方法,虽然我仍然想问一下以供学习。
所以我遇到了这个问题,我正在为一些记录数据制作编辑器屏幕,并且在该记录中有一个名为 'Quantity' 的字段。然而,在设计时,它被做成一个数量占位符,但它意味着不同的东西。所以解释一下,它是一个 SkuReference table,它有一个 'Type' 定义它是 'Quantity per Pack'、'Roll Length' 还是 'CBC'。好吧,对于 'Quantity per Pack' 和 'Roll Length',一个简单的数字就可以工作,但是对于 'CBC'(意思是 Corners/Borders/Centers),数据存储为 JSON 字符串对象:
{ 'Corners': 10, 'Borders': 20, 'Centers': 30 }
现在在 WPF 屏幕上,如果数据被识别为 'CBC',我将数据路由到三个文本框,都绑定到父对象的 'Quantity' 属性并使用转换器和参数来识别每一个,然后我将适当的值放入每个文本框。工作正常。
我遇到的问题是在尝试使用转换器的 ConvertBack 部分时。我意识到我没有对原始字符串 属性 的引用,我可以编辑并向其提供新值,或者访问其他文本框以将新字符串重建为 return。我试图想出一个解决方案,也许在我的脑海中使用 MultiBinding,但无法完全得出答案。
这可能吗?顺便说一句,我最终只是创建了被拆分的新属性,并且在设置父对象时解析并传递了数据。但是,为了将来参考,在我看来只使用原始数据和转换器而不进行额外工作似乎更干净。
下面是其他代码供参考:
XAML,UpsertSkuReference.Quantity就是上面的JSON串
<StackPanel Grid.Column="5">
<TextBlock Text="CBC" />
<StackPanel Orientation="Horizontal">
<TextBox Width="30" Text="{Binding UpsertSkuReference.Quantity, ConverterParameter=co, Converter={StaticResource CBCToIndividualConverter}}" IsEnabled="{Binding CBCIsChecked}" />
<TextBox Width="30" Margin="5,0,0,0" Text="{Binding UpsertSkuReference.Quantity, ConverterParameter=b, Converter={StaticResource CBCToIndividualConverter}}" IsEnabled="{Binding CBCIsChecked}" />
<TextBox Width="30" Margin="5,0,0,0" Text="{Binding UpsertSkuReference.Quantity, ConverterParameter=ce, Converter={StaticResource CBCToIndividualConverter}}" IsEnabled="{Binding CBCIsChecked}" />
</StackPanel>
</StackPanel>
转换器
public class CBCToIndividualConverter : IValueConverter
{
JavaScriptSerializer json = new JavaScriptSerializer();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//--value = CBC JSON object string
//--parameter = [co]: Corners, [b]: Borders, [ce]: Centers
if (value != null)
{
if (parameter == null) { throw new Exception("CBCToIndividualConverter: parameter cannot be null"); }
if (new string[] { "co", "b", "ce" }.Contains(parameter.ToString().ToLower()) == false)
{ throw new Exception("CBCToIndividualConverter: parameter must be 'co' for Corners, 'b' for Borders, or 'ce' for Centers"); }
CornerBorderCenterModel cbc = json.Deserialize<CornerBorderCenterModel>(value.ToString());
switch (parameter.ToString().ToLower())
{
case "co": { return cbc.Corners; }
case "b": { return cbc.Borders; }
case "ce": { return cbc.Centers; }
default: { return null; }
}
}
else { return null; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
//--value = number for parameter type
//--parameter = [co]: Corners, [b]: Borders, [ce]: Centers
//--?? Uh Oh
}
return null;
}
}
转换器不应该那样使用。有更清洁的方法。我建议您进行稍微大一点的重构:
- 在包含字符串
Quantity
(SkuReference
? );
- 设置其中任何一个时,更新
Quantity
;当您更新 Quantity
时,您尝试在 CornerBorderCenterModel
实例中进行解析,然后使用该实例的值更新 3 个属性。所有这些工作都是为了实现 OnPropertyChanged
方法(稍后查看我的代码);
- 在视图中,将每个
TextBox
与相对 属性 绑定。这样,您根本不需要转换器(如果外部 DataContext
是 UpsertSkuReference
的实例,则此方法有效;否则,您必须设置 StackPanel
的 DataContext
this方式:DataContext="{Binding UpsertSkuReference}"
,所以3TextBoxes
的绑定可以找到对象上的属性。
整轮:
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
namespace XXX
{
public class CornerBorderCenterModel
{
public int Corners { get; set; }
public int Borders { get; set; }
public int Centers { get; set; }
}
public class SkuReference : INotifyPropertyChanged
{
static JavaScriptSerializer json = new JavaScriptSerializer();
private static string[] _jsonStringParts =
new[] { nameof(Borders), nameof(Corners), nameof(Centers) };
public SkuReference()
{
PropertyChanged += OnPropertyChanged;
}
public int Corners
{
get { return _Corners; }
set
{
if (_Corners != value)
{
_Corners = value;
RaisePropertyChanged();
}
}
}
private int _Corners;
public int Borders
{
get { return _Borders; }
set
{
if (_Borders != value)
{
_Borders = value;
RaisePropertyChanged();
}
}
}
private int _Borders;
public int Centers
{
get { return _Centers; }
set
{
if (_Centers != value)
{
_Centers = value;
RaisePropertyChanged();
}
}
}
private int _Centers;
private void UpdateCBCFromQuantity()
{
//if Quantity is a CBC and is not null do the following:
var cbc = json.Deserialize<CornerBorderCenterModel>(_Quantity.ToString());
if (cbc != null)
{
Corners = cbc.Corners;
Borders = cbc.Borders;
Centers = cbc.Centers;
}
}
public string Quantity
{
get { return _Quantity; }
set
{
if (_Quantity != value)
{
_Quantity = value;
RaisePropertyChanged();
}
}
}
private string _Quantity;
private void UpdateJsonStringFromCBC()
{
Quantity = string.Format(
"{{ 'Corners': {0}, 'Borders': {1}, 'Centers': {2} }}",
_Corners,
_Borders,
_Centers);
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_jsonStringParts.Contains(e.PropertyName))
UpdateJsonStringFromCBC();
else if (e.PropertyName == nameof(Quantity))
UpdateCBCFromQuantity();
}
}
}
<StackPanel Orientation="Horizontal" DataContext="{Binding UpsertSkuReference}">
<TextBox Text="{Binding Corners}" IsEnabled="{Binding CBCIsChecked}" />
<TextBox Text="{Binding Borders}" IsEnabled="{Binding CBCIsChecked}" />
<TextBox Text="{Binding Centers}" IsEnabled="{Binding CBCIsChecked}" />
</StackPanel>
请注意,Quantity
、Borders
、Corners
和 Centers
必须在更改时发出通知(就像我做的那样,或者使用更高级的工具,如 Reactive library),否则整轮都无法进行。
由于工作时间有限,我已经针对这个问题做了一个解决方法,虽然我仍然想问一下以供学习。
所以我遇到了这个问题,我正在为一些记录数据制作编辑器屏幕,并且在该记录中有一个名为 'Quantity' 的字段。然而,在设计时,它被做成一个数量占位符,但它意味着不同的东西。所以解释一下,它是一个 SkuReference table,它有一个 'Type' 定义它是 'Quantity per Pack'、'Roll Length' 还是 'CBC'。好吧,对于 'Quantity per Pack' 和 'Roll Length',一个简单的数字就可以工作,但是对于 'CBC'(意思是 Corners/Borders/Centers),数据存储为 JSON 字符串对象:
{ 'Corners': 10, 'Borders': 20, 'Centers': 30 }
现在在 WPF 屏幕上,如果数据被识别为 'CBC',我将数据路由到三个文本框,都绑定到父对象的 'Quantity' 属性并使用转换器和参数来识别每一个,然后我将适当的值放入每个文本框。工作正常。
我遇到的问题是在尝试使用转换器的 ConvertBack 部分时。我意识到我没有对原始字符串 属性 的引用,我可以编辑并向其提供新值,或者访问其他文本框以将新字符串重建为 return。我试图想出一个解决方案,也许在我的脑海中使用 MultiBinding,但无法完全得出答案。
这可能吗?顺便说一句,我最终只是创建了被拆分的新属性,并且在设置父对象时解析并传递了数据。但是,为了将来参考,在我看来只使用原始数据和转换器而不进行额外工作似乎更干净。
下面是其他代码供参考:
XAML,UpsertSkuReference.Quantity就是上面的JSON串
<StackPanel Grid.Column="5">
<TextBlock Text="CBC" />
<StackPanel Orientation="Horizontal">
<TextBox Width="30" Text="{Binding UpsertSkuReference.Quantity, ConverterParameter=co, Converter={StaticResource CBCToIndividualConverter}}" IsEnabled="{Binding CBCIsChecked}" />
<TextBox Width="30" Margin="5,0,0,0" Text="{Binding UpsertSkuReference.Quantity, ConverterParameter=b, Converter={StaticResource CBCToIndividualConverter}}" IsEnabled="{Binding CBCIsChecked}" />
<TextBox Width="30" Margin="5,0,0,0" Text="{Binding UpsertSkuReference.Quantity, ConverterParameter=ce, Converter={StaticResource CBCToIndividualConverter}}" IsEnabled="{Binding CBCIsChecked}" />
</StackPanel>
</StackPanel>
转换器
public class CBCToIndividualConverter : IValueConverter
{
JavaScriptSerializer json = new JavaScriptSerializer();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//--value = CBC JSON object string
//--parameter = [co]: Corners, [b]: Borders, [ce]: Centers
if (value != null)
{
if (parameter == null) { throw new Exception("CBCToIndividualConverter: parameter cannot be null"); }
if (new string[] { "co", "b", "ce" }.Contains(parameter.ToString().ToLower()) == false)
{ throw new Exception("CBCToIndividualConverter: parameter must be 'co' for Corners, 'b' for Borders, or 'ce' for Centers"); }
CornerBorderCenterModel cbc = json.Deserialize<CornerBorderCenterModel>(value.ToString());
switch (parameter.ToString().ToLower())
{
case "co": { return cbc.Corners; }
case "b": { return cbc.Borders; }
case "ce": { return cbc.Centers; }
default: { return null; }
}
}
else { return null; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
//--value = number for parameter type
//--parameter = [co]: Corners, [b]: Borders, [ce]: Centers
//--?? Uh Oh
}
return null;
}
}
转换器不应该那样使用。有更清洁的方法。我建议您进行稍微大一点的重构:
- 在包含字符串
Quantity
(SkuReference
? ); - 设置其中任何一个时,更新
Quantity
;当您更新Quantity
时,您尝试在CornerBorderCenterModel
实例中进行解析,然后使用该实例的值更新 3 个属性。所有这些工作都是为了实现OnPropertyChanged
方法(稍后查看我的代码); - 在视图中,将每个
TextBox
与相对 属性 绑定。这样,您根本不需要转换器(如果外部DataContext
是UpsertSkuReference
的实例,则此方法有效;否则,您必须设置StackPanel
的DataContext
this方式:DataContext="{Binding UpsertSkuReference}"
,所以3TextBoxes
的绑定可以找到对象上的属性。
整轮:
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
namespace XXX
{
public class CornerBorderCenterModel
{
public int Corners { get; set; }
public int Borders { get; set; }
public int Centers { get; set; }
}
public class SkuReference : INotifyPropertyChanged
{
static JavaScriptSerializer json = new JavaScriptSerializer();
private static string[] _jsonStringParts =
new[] { nameof(Borders), nameof(Corners), nameof(Centers) };
public SkuReference()
{
PropertyChanged += OnPropertyChanged;
}
public int Corners
{
get { return _Corners; }
set
{
if (_Corners != value)
{
_Corners = value;
RaisePropertyChanged();
}
}
}
private int _Corners;
public int Borders
{
get { return _Borders; }
set
{
if (_Borders != value)
{
_Borders = value;
RaisePropertyChanged();
}
}
}
private int _Borders;
public int Centers
{
get { return _Centers; }
set
{
if (_Centers != value)
{
_Centers = value;
RaisePropertyChanged();
}
}
}
private int _Centers;
private void UpdateCBCFromQuantity()
{
//if Quantity is a CBC and is not null do the following:
var cbc = json.Deserialize<CornerBorderCenterModel>(_Quantity.ToString());
if (cbc != null)
{
Corners = cbc.Corners;
Borders = cbc.Borders;
Centers = cbc.Centers;
}
}
public string Quantity
{
get { return _Quantity; }
set
{
if (_Quantity != value)
{
_Quantity = value;
RaisePropertyChanged();
}
}
}
private string _Quantity;
private void UpdateJsonStringFromCBC()
{
Quantity = string.Format(
"{{ 'Corners': {0}, 'Borders': {1}, 'Centers': {2} }}",
_Corners,
_Borders,
_Centers);
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_jsonStringParts.Contains(e.PropertyName))
UpdateJsonStringFromCBC();
else if (e.PropertyName == nameof(Quantity))
UpdateCBCFromQuantity();
}
}
}
<StackPanel Orientation="Horizontal" DataContext="{Binding UpsertSkuReference}">
<TextBox Text="{Binding Corners}" IsEnabled="{Binding CBCIsChecked}" />
<TextBox Text="{Binding Borders}" IsEnabled="{Binding CBCIsChecked}" />
<TextBox Text="{Binding Centers}" IsEnabled="{Binding CBCIsChecked}" />
</StackPanel>
请注意,Quantity
、Borders
、Corners
和 Centers
必须在更改时发出通知(就像我做的那样,或者使用更高级的工具,如 Reactive library),否则整轮都无法进行。