根据另一个绑定在DataContext中选择不同的绑定

时间:2017-05-03 00:35:46

标签: c# wpf xaml

我有一个相当动态的ObservableCollection视图模型,它由XAML中的两个不同的ListBox元素使用。每个视图模型都包含两个名为CardPrimary的{​​{1}}类型的不同模型对象的属性,以及其他属性。在一个ListBox中,我想显示来自Secondary的属性,而另一个我希望显示来自Primary的属性。在为两者显示ListBoxItem时,我想使用相同的XAML UserControl文件。

我的第一个想法是在UserControl.Resources中创建一个条目,该条目根据来自父视图模型的RelativeSource引用为“右”卡提供一个名称,该引用表示SecondaryPrimary,但是我之前没有创建过这样的条目。这是正确的方法吗?如果是这样,条目会是什么样的?

我已经制作了一些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>

1 个答案:

答案 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>

enter image description here

我最初想到的是一个更精细的方案,你给视图一个DataTemplate,它工作,但这更简单。另一方面,这更强大。在我理解之前,我实际上在答案的第一个版本中使用了它;它在编辑历史中。

感谢一个有趣的小项目。