在 ViewModel 中覆盖 List.ToString()
Override List.ToString() in ViewModel
我有一个显示部件的数据网格。每个部分都可以有多个标识符,网格看起来像这样
Name Identifiers Size ...
-------------------------
P1 A,B 3
P2 C,D 4
P3 E 2
在我的模型中,标识符存储在每个零件的列表中
class PartListModel {
public ObservableCollection<Part> Parts { get; set; }
}
class Part {
public IList<Identification> Identifications { get; set; }
}
但是为了让它在我的视图中正确显示,我必须在我的部分
中添加一个 Getter
class Part {
public string IdentNames {
get { return string.Join(",", Identifications.Select(i => i.ArticleNumber)); }
}
}
在我的视图中显示它
<DataGridTextColumn Header="Identifiers" Binding="{Binding IdentNames}" />
这是我想避免的黑客行为。如何让我的 ViewModel 覆盖我的列表的 ToString()
方法而不需要在我的模型中添加丑陋的 Getter?
一个选项(您可能会使其更通用以处理不同的情况)是创建一个值转换器来输出所需的值:
public class MyIdentificationConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return string.Join(",",
((List<MainWindowViewModel.Identification>)value).Select(x => x.ArticleNumber));
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
然后在您的 XAML:
中使用它
<Window.Resources>
<myWpfApp:MyIdentificationConverter x:Key="MyIdentificationConverter" />
</Window.Resources>
...
<DataGridTextColumn Header="Identifiers" Binding="{Binding Path=Identifications,
Converter={StaticResource MyIdentificationConverter}}" />
这是一个更通用的版本,使用反射找到正确的 属性:
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return string.Join(",",
(((IEnumerable<object>)value).Select(x => x.GetType()
.GetProperty(parameter.ToString()).GetValue(x))));
}
再次从 XAML 调用:(将 属性 作为参数传递给 select)
<DataGridTextColumn Header="Identifiers" Binding="{Binding Path=Identifications,
Converter={StaticResource MyIdentificationConverter},
ConverterParameter=ArticleNumber}" />
我写了IEnumerable<T>
的扩展方法,大家可以添加使用:
public static string ToString<T>(this IEnumerable<T> values,string seperator,Func<T,string> property)
{
return string.Join(seperator, values.Select(property));
}
并像这样使用它:
List<PhoneDictionary> table = new List<PhoneDictionary>()
{
new PhoneDictionary { Phone1 = 1, Phone2 =2},
new PhoneDictionary { Phone1 = 1, Phone2 =2},
new PhoneDictionary { Phone1 = 2, Phone2 =3},
new PhoneDictionary { Phone1 = 3, Phone2 =4},
new PhoneDictionary { Phone1 = 3, Phone2 =4},
};
var phones = table.ToString(",",x => x.Phone1.ToString());
输出:
1,1,2,3,3
我的Class:
public class PhoneDictionary
{
public int Phone1 { get; set; }
public int Phone2 { get; set; }
}
在你的情况下你可以这样调用:
string articles = partsViewModel.Parts.ToString(",",x=>x.ArticleNumber);
您可以通过编写本身也是值转换器的标记扩展来获得更好的 XAML 语法。
[MarkupExtensionReturnType(typeof(BindingBase))]
[ValueConversion(typeof(object), typeof(string))]
public sealed class ConcatenateExtension : MarkupExtension, IValueConverter
{
public ConcatenateExtension()
{
Separator = ",";
}
public ConcatenateExtension(string path)
: this()
{
Path = path;
}
public string Path { get; set; }
public string Property { get; set; }
public string Separator { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding(Path);
binding.Converter = this;
return binding;
}
object IValueConverter.Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
// 'value' is the property value obtained by binding to `Path`
// We're assuming here that 'value' can be enumerated
// Code will obviously throw an exception if it can't
IEnumerable<object> sequence =
from object element in value as IEnumerable<object>
let propertyValue =
Property == null ?
element :
element.GetType().GetProperty(Property).GetValue(element)
select propertyValue;
return String.Join(Separator, sequence);
}
object IValueConverter.ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
您可以像这样在 XAML 中使用它:
xmlns:my="clr-namespace:..."
...
<DataGridTextColumn Header="Ids" Binding="{my:Concatenate
Identifications, Property=ArticleNumber, Separator=' / '}" />
Separator
属性 是可选的,默认为单个逗号。
不幸的是,代码仍然使用反射,但我看不出有什么方法可以避免它。
我有一个显示部件的数据网格。每个部分都可以有多个标识符,网格看起来像这样
Name Identifiers Size ...
-------------------------
P1 A,B 3
P2 C,D 4
P3 E 2
在我的模型中,标识符存储在每个零件的列表中
class PartListModel {
public ObservableCollection<Part> Parts { get; set; }
}
class Part {
public IList<Identification> Identifications { get; set; }
}
但是为了让它在我的视图中正确显示,我必须在我的部分
中添加一个 Getterclass Part {
public string IdentNames {
get { return string.Join(",", Identifications.Select(i => i.ArticleNumber)); }
}
}
在我的视图中显示它
<DataGridTextColumn Header="Identifiers" Binding="{Binding IdentNames}" />
这是我想避免的黑客行为。如何让我的 ViewModel 覆盖我的列表的 ToString()
方法而不需要在我的模型中添加丑陋的 Getter?
一个选项(您可能会使其更通用以处理不同的情况)是创建一个值转换器来输出所需的值:
public class MyIdentificationConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return string.Join(",",
((List<MainWindowViewModel.Identification>)value).Select(x => x.ArticleNumber));
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
然后在您的 XAML:
中使用它<Window.Resources>
<myWpfApp:MyIdentificationConverter x:Key="MyIdentificationConverter" />
</Window.Resources>
...
<DataGridTextColumn Header="Identifiers" Binding="{Binding Path=Identifications,
Converter={StaticResource MyIdentificationConverter}}" />
这是一个更通用的版本,使用反射找到正确的 属性:
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return string.Join(",",
(((IEnumerable<object>)value).Select(x => x.GetType()
.GetProperty(parameter.ToString()).GetValue(x))));
}
再次从 XAML 调用:(将 属性 作为参数传递给 select)
<DataGridTextColumn Header="Identifiers" Binding="{Binding Path=Identifications,
Converter={StaticResource MyIdentificationConverter},
ConverterParameter=ArticleNumber}" />
我写了IEnumerable<T>
的扩展方法,大家可以添加使用:
public static string ToString<T>(this IEnumerable<T> values,string seperator,Func<T,string> property)
{
return string.Join(seperator, values.Select(property));
}
并像这样使用它:
List<PhoneDictionary> table = new List<PhoneDictionary>()
{
new PhoneDictionary { Phone1 = 1, Phone2 =2},
new PhoneDictionary { Phone1 = 1, Phone2 =2},
new PhoneDictionary { Phone1 = 2, Phone2 =3},
new PhoneDictionary { Phone1 = 3, Phone2 =4},
new PhoneDictionary { Phone1 = 3, Phone2 =4},
};
var phones = table.ToString(",",x => x.Phone1.ToString());
输出:
1,1,2,3,3
我的Class:
public class PhoneDictionary
{
public int Phone1 { get; set; }
public int Phone2 { get; set; }
}
在你的情况下你可以这样调用:
string articles = partsViewModel.Parts.ToString(",",x=>x.ArticleNumber);
您可以通过编写本身也是值转换器的标记扩展来获得更好的 XAML 语法。
[MarkupExtensionReturnType(typeof(BindingBase))]
[ValueConversion(typeof(object), typeof(string))]
public sealed class ConcatenateExtension : MarkupExtension, IValueConverter
{
public ConcatenateExtension()
{
Separator = ",";
}
public ConcatenateExtension(string path)
: this()
{
Path = path;
}
public string Path { get; set; }
public string Property { get; set; }
public string Separator { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding(Path);
binding.Converter = this;
return binding;
}
object IValueConverter.Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
// 'value' is the property value obtained by binding to `Path`
// We're assuming here that 'value' can be enumerated
// Code will obviously throw an exception if it can't
IEnumerable<object> sequence =
from object element in value as IEnumerable<object>
let propertyValue =
Property == null ?
element :
element.GetType().GetProperty(Property).GetValue(element)
select propertyValue;
return String.Join(Separator, sequence);
}
object IValueConverter.ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
您可以像这样在 XAML 中使用它:
xmlns:my="clr-namespace:..."
...
<DataGridTextColumn Header="Ids" Binding="{my:Concatenate
Identifications, Property=ArticleNumber, Separator=' / '}" />
Separator
属性 是可选的,默认为单个逗号。
不幸的是,代码仍然使用反射,但我看不出有什么方法可以避免它。