我的应用程序中有以下界面:
public interface IContactMedium
{
string ContactString { get; set; }
string Type { get; set;}
bool IsValid();
}
此界面用于表示某人的某种联系的对象。它可以是电话,电子邮件等。ContactString
属性是实际的联系人数据(例如,对于电话,它将是电话号码),Type
用于区分情况一个人有一个以上(对于电话,一个人可以有家庭电话,工作电话,手机等)IsValid
方法是每种不同类型的联系媒介的验证机制。
所以,让我们说我的应用程序中有两个对象 - Email
和Phone
- 都实现了接口。我将在应用程序中创建一个UserControl
,其中包含管理此类对象列表的UI。所以viewmodel看起来像这样:
public class ContactsCollectionViewModel<T> : ViewModelBase where T : class, IContactMedium
{
private ObservableCollection<T> _itemsCollection;
public ContactCollectionViewModel(ObservableCollection<T> items)
{
ItemsCollection = items;
}
public ObservableCollection<T> ItemsCollection
{
get { return _itemsCollection; }
set
{
if (_itemsCollection != value)
{
_itemsCollection = value;
OnPropertyChanged(() => ItemsCollection);
}
}
}
}
我想在IContactMedium
接口中添加另一个属性/方法,该属性/方法在WPF中的Binding中使用时为ContactString
属性提供正确的格式。这个想法是绑定到ContactString
的文本框中的格式根据实际存储在集合中的具体对象而有所不同:
<TextBox x:Name="ContactString"
Text="{Binding ContactString, StringFormat=???}" />
我在网上搜索了一个解决方案,但找不到任何东西。我看到人们建议修改ContactString
属性,以便getter返回格式化的值。因此,对于Phone
对象,例如,属性将如下所示:
public string ContactString
{
get
{
return string.Format("({0}) {1}-{2}", _contactString.Substring(0,3), _contactString.Substring(4,3), _contactString.Substring(7,3));
}
set {
_contactString = value;
}
}
然而,对我来说这不是一个好的解决方案。该信息不仅由UI使用。它还会发送到应用程序的其他部分,包括数据库,需要原始格式的电话号码:##########。
有没有办法为XAML提供格式化程序,以便在绑定的StringFormat
属性中使用?格式化是否可以由实现接口的对象决定?如果是,那么它需要什么类型,以及如何让XAML中的Binding
可以访问它?
答案 0 :(得分:3)
格式化是否可以由实现接口的对象决定?
在Xaml中,可以提供与特定类关联的数据模板。
只需在模板中为结构提供与目标属性绑定的格式,如下所示:
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type c:Ship}">
<TextBlock Text="{Binding Path=Name, StringFormat=Ship: {0}}"
Foreground="Red" />
</DataTemplate>
<DataTemplate DataType="{x:Type c:Passage}">
<TextBlock Text="{Binding Path=Name, StringFormat=Passage: {0}}"
Foreground="Blue" />
</DataTemplate>
</Grid.Resources>
<ListBox Name="myListBox"
Height="300"
Width="200"
ItemsSource="{Binding OBSCollection}">
</ListBox>
</Grid>
因此对于我的集合,Ship
和Passage
的类实例都遵循ITreeEntity
:
public ObservableCollection<ITreeEntity> OBSCollection ...
当绑定创建一个列表,其中绑定具有特定的字符串格式:
请注意,在设置数据时,首先添加船只,然后添加通道。无论如何,Xaml都没有订购它们。
需要从复合集合中列出一个ListBox中的不同类型对象吗?在这里查看我的答案:
答案 1 :(得分:0)
您可以使用转换器。保持你的财产简单。
public string ContactString { get; set; }
实施转换器
class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
contactString = value as string;
if(contactString == null)
{
throw new InvalidArgumentException();
}
return string.Format("({0}) {1}-{2}",
contactString.Substring(0,3), contactString.Substring(4,3),
contactString.Substring(7,3));
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
将其添加为资源
<Window.Resources>
<local:MyConverter x:Key="MyConverter"/>
</Window.Resources>
使用它
<TextBox x:Name="ContactString"
Text="{Binding ContactString, Converter{StaticResource MyConverter}}" />
答案 2 :(得分:-1)
您可以简单地覆盖ToString()
方法。默认情况下,ListBox
将使用对象的ToString()
方法作为项目的显示文本。
public override string ToString()
{
return string.Format("({0}) {1}-{2}", _contactString.Substring(0,3), _contactString.Substring(4,3), _contactString.Substring(7,3));
}
这意味着您无需在ListBox
中执行任何奇特的操作,例如定义DataTemplate
,因为ListBox
会自动获取格式化的字符串。
<ListBox Name="myListBox"
Height="300"
Width="200"
ItemsSource="{Binding OBSCollection}"/>
答案 3 :(得分:-1)
事实是,实现接口的每个具体类都有不同的格式规则
格式化是否可以由实现接口的对象决定?
困境是,是否将格式化逻辑添加到业务对象(IContactMedium
实现)或表示层。
如果是业务逻辑,那么是的,您应该将格式代码添加到业务对象中。
但最有可能的是表现逻辑。在这种情况下,要么创建DataTemplate foreach实现IContactMedium
,要么创建转换器。在转换器中,您可以根据值类型选择正确的格式。如果输出只是纯文本,请使用转换器。如果它更纯文本,例如格式化文本,则使用datatemplates。
提示:您可以使用单元测试来测试IContactMedium
的所有实现是否都有其DataTemplate,或者是否由转换器覆盖。