想象一下以下简单的模型(出于简单原因的例子;实际上,我们这里有MVVM,但没关系):
public class User {
public string Username { get; set; }
}
public class StackOverflowUser : User {
public int Reputation { get; set; }
}
现在我们有一个Silverlight UserControl
,其中包含以下Controls
(再次,这只是一个示例,剥离到核心):
<Grid>
<TextBlock Text="Username:" />
<TextBlock Text="{Binding Path=Username}" />
<TextBlock Text="Reputation:" />
<TextBlock Text="{Binding Path=Reputation}" />
</Grid>
现在我希望此UserControl
与这两个模型User
和StackOverflowUser
兼容。我可以将UserControl的DataContext
设置为User
或StackOverflowUser
类型:
this.DataContext = new User { Username = "john.doe" };
如果设置为StackOverflowUser
,一切正常。如果设置为User
,则会收到“BindingExpression Path错误”,因为Reputation
模型中缺少属性User
。我完全理解。
有没有办法1)避免这种情况 例外和2)控制 崩溃的可见性 当绑定属性不可用时?
当然,我们更喜欢优雅的解决方案,通过调整绑定表达式和/或使用转换器等解决问题,并尽可能避免大量代码。
提前感谢您的帮助和建议,
最好的问候,
托马斯
答案 0 :(得分:1)
不幸的是,Silverlight对DataTemplates的多态行为有限,我只能想到一个解决方法:
也为User类提供属性声誉,但使其无意义,例如-1。然后将样式应用于声誉TextBlocks:
<Page.Resources>
<Style Key="Reputation">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Reputation} Value="-1">
<Setter Property="Visibility" Value="Invisible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Page.Resources>
...
<TextBlock Text="Reputation:" Style="{StaticResource Reputation}">
<TextBlock Text="{Binding Path=Reputation}" Style="{StaticResource Reputation}">
您也可以尝试(我无法测试):
将其DataTrigger绑定到类型标识属性,并将{Binding Path = Reputation}声明移动到Setter:
<Style Key="ReputationContent">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Type} Value="StackOverflow">
<Setter Property="Visibility" Value="Invisible" />
<Setter Property="Text" Value="{Binding Path=Reputation}" />
</DataTrigger>
</Style.Triggers>
</Style>
但是你看,没有优雅的方式,遗憾的是DataTemplate在Silverlight中没有DataType属性。
答案 1 :(得分:1)
答案 2 :(得分:0)
我终于解决了我的问题。一位同事最终实现了一个解决方案,其中包括WPF DataTemplates DataType属性(或通常是DataTemplateSelector)的解决方法。它不是很漂亮(我猜,没有解决办法)但它的确有效。不幸的是,我不能发布任何代码片段,因为它是封闭源代码。但我之后发现了一些链接,提供了一个非常类似的解决方案,如下所示:Silverlight: a port of the DataTemplateSelector。如果您遇到类似问题,这也会对您有所帮助。 Here或there对此主题有更多的想法。
实际的解决方案是遵循Ozan的提示。不幸的是,他的解决方案不起作用,所以我不想将他的评论标记为已接受的答案,但我至少给了一个upvote。
谢谢!
最好的问候,
托马斯
答案 3 :(得分:0)
我知道这已经得到了解答,但我仍然认为它值得这篇文章。使用反射,您可以在ViewModel中拥有一个属性,该属性可以轻松处理有时只具有该属性的Dto对象。反思可能很昂贵,所以要根据你的决定权衡。
public int? Reputation
{
get
{
var prop = Dto.GetType().GetProperty("Reputation");
return (prop != null)? (int)prop.GetValue(Dto, null) : null;
}
set
{
var prop = Dto.GetType().GetProperty("Reputation");
if(prop !=null) prop.SetValue(Dto,value, null);
}
}