我有一个相当动态的ObservableCollection视图模型,它由XAML中的两个不同的ListBox元素使用。每个视图模型都包含两个名为Card
和Primary
的{{1}}类型的不同模型对象的属性,以及其他属性。在一个ListBox中,我想显示来自Secondary
的属性,而另一个我希望显示来自Primary
的属性。在为两者显示ListBoxItem时,我想使用相同的XAML UserControl文件。
我的第一个想法是在UserControl.Resources中创建一个条目,该条目根据来自父视图模型的RelativeSource引用为“右”卡提供一个名称,该引用表示Secondary
或Primary
,但是我之前没有创建过这样的条目。这是正确的方法吗?如果是这样,条目会是什么样的?
我已经制作了一些XAML来帮助说明(可能有拼写错误)。首先,主ListBox控件:
Secondary
中学:
<UserControl x:Class="Project.Cards.ListPrimary" d:DataContext="{Binding Main.Cards.Primary, Source={StaticResource Locator}}">
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type vms:CardViewModel}">
<views:Card />
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<ListBox x:Name="CardListBox"
SelectedItem="{Binding SelectedCard}"
ItemsSource="{Binding Cards}" />
</Grid>
</UserControl>
卡片视图(我需要将“Primary.Direction”替换为允许我选择Primary / Secondary的东西):
<UserControl x:Class="Project.Cards.ListSecondary" d:DataContext="{Binding Main.Cards.Secondary, Source={StaticResource Locator}}">
... (same) ...
</UserControl>
答案 0 :(得分:1)
如果您希望同一UserControl的两个实例在一个方面有所不同,那么您将弄清楚如何对其进行参数化。有几种方法,但我认为最简单的方法就是将不同的值绑定到View的属性。这会将不同值的规范移动到所有者。
我们通过在UserControl上定义依赖项属性来做到这一点。它是一个字符串,虽然它可能是一个对象,并且将来你可能想把它变成一个。由于我们在DataTemplate中使用视图,因此我们可以将DataContext的属性绑定到那里。
public partial class Card : UserControl
{
public Card()
{
InitializeComponent();
}
public String Direction
{
get { return (String)GetValue(DirectionProperty); }
set { SetValue(DirectionProperty, value); }
}
public static readonly DependencyProperty DirectionProperty = DependencyProperty.Register("Direction",
typeof(String), typeof(Card), new PropertyMetadata(null));
}
...我们将在UserControl中使用它,如下所示:
<Grid>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Description}" />
<TextBlock
Text="{Binding Direction, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
</StackPanel>
</Grid>
RelativeSource
内容告诉Binding
在UserControl对象本身上查找Direction
属性,而不是DataContext
,就像默认情况下那样。
如果Card.Direction
是对象而不是字符串,则您将TextBox设为ContentControl并绑定到其Content属性。然后你可以把任何放在那里 - XAML,一个完整的其他视图模型,字面上是XAML可以找出如何显示的任何东西。
以及它在野外的样子:
<DataTemplate DataType="{x:Type vms:CardViewModel}">
<views:Card Direction="{Binding Primary.Direction}" />
</DataTemplate>
这是我测试代码中的所有主窗口内容。我没有打扰为列表框创建用户控件;上述模板与您执行此操作的方式完全匹配。
<Window.Resources>
<DataTemplate x:Key="PrimaryItemTemplate" DataType="{x:Type vms:CardViewModel}">
<views:Card Direction="{Binding Primary.Direction}" />
</DataTemplate>
<DataTemplate x:Key="SecondaryItemTemplate" DataType="{x:Type vms:CardViewModel}">
<views:Card Direction="{Binding Secondary.Direction}" />
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal">
<ListBox
SelectedItem="{Binding SelectedCard}"
ItemsSource="{Binding Cards}"
ItemTemplate="{StaticResource PrimaryItemTemplate}"
/>
<ListBox
SelectedItem="{Binding SelectedCard}"
ItemsSource="{Binding Cards}"
ItemTemplate="{StaticResource SecondaryItemTemplate}"
/>
</StackPanel>
</Grid>
我最初想到的是一个更精细的方案,你给视图一个DataTemplate,它工作,但这更简单。另一方面,这更强大。在我理解之前,我实际上在答案的第一个版本中使用了它;它在编辑历史中。
感谢一个有趣的小项目。