【问题标题】:WPF Bind to property of current element and parent elementWPF 绑定到当前元素和父元素的属性
【发布时间】:2017-07-20 00:48:21
【问题描述】:

我对 WPF 非常陌生,并且在创建绑定时尝试使用 Knockout JS 作为类比。我听说 WPF 的绑定引擎更强大,但我正在努力解决在 Knockout 中很容易解决的问题。

假设我有一个包含地址集合的 Person 类。

public class Person
{
    public string PersonId {get;set;}
    public IList<Address> Addresses{get; set;}
    public string FormattedName{get;set;}
}

public class Address
{
    public string AddressId{get;set;}
    public string Address1{get;set;}
    public string City{get;set;}
    public string State{get;set;}
    public string Zip{get;set;}
}

假设在一个页面上,我有一组人,对于每个人,我想显示所有地址并提供用于选择地址的按钮。所以,我的页面视图模型如下所示:

public class AddressSelection
{
    public string PersonId{get;set;}
    public string AddressId{get;set;}
}

public class PersonAddressSelectionViewModel
{
    public IList<Person> People {get; set;}

    public Person SelectedPerson {get;set;}
    public Address SelectedAddress{get;set;}

    public void SelectAddress(string personId, string addressId)
    {
        this.SelectedPerson = this.People.FirstOrDefault(x => x.PersonId == personId);
        this.SelectedAddress = this.SelectedPerson?.Addresses.FirstOrDefault(x => x.AddressId == addressId);
    }

    public void SelectAddress(AddressSelection arg)
    {  SelectAddress(arg.PersonId, arg.AddressId); }
}

现在,我想显示一个 UI,其中显示每个乘客的标题,然后显示每个地址的按钮。选择按钮时,应触发 SelectAddress 函数。但是,在 XAML 中,我不确定如何分配绑定以同时使用父元素和当前元素属性;它是否可以从它们中创建一个对象,甚至只是调用一个方法并从两者中获取参数。

在淘汰赛中,您只需绑定函数并访问父上下文,例如:

<!-- ko foreach: $data.people -->
<h2 data-bind="text: formattedName"></h2>
<ul data-bind="foreach: $data.addresses">
    <li>
        <button data-bind="click: function() { $parents[1].selectAddress($parent.personId, $data.addressId); }">Select Address</button>
    </li>
</ul>
<!-- /ko -->

我似乎不知道如何在 XAML 中做同样的事情。

<ItemsControl Grid.Row="2" ItemsSource="{x:Bind ViewModel.People, Mode=OneWay}"
    HorizontalAlignment="Center" Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="5">
                <TextBlock Style="{StaticResource TextBlockSmallStyle}" VerticalAlignment="Center" Width="180" Text="{Binding FormattedName, Mode=OneWay}" />
                <ItemsControl ItemsSource="{Binding Addresses}" Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <VirtualizingStackPanel Orientation="Horizontal" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Button Width="180" Style="{StaticResource ButtonStyle}"
                                Content="{Binding Address1}"
                                Click="SelectAddressClicked" Tag="{Binding **what to put here**}" />
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

在后面的代码中:

SelectAddressClicked(object sender, object args) {
    ((Button)sender).Tag as Address; // would prefer this to be something that has both the address id and person id
}

【问题讨论】:

  • 你不需要绑定到标签。单击按钮的数据上下文是地址。 ((Button)sender).DataContext 作为地址。
  • @MarkW 感谢您的提示!

标签: c# wpf xaml data-binding


【解决方案1】:

您应该使用 MVVM 而不是后面的代码来执行此操作

谷歌RelayCommand类实现

然后在您的视图模型中添加以下定义

public RelayCommand SelectAddressCommand { get; private set; }

在构造函数中

SelectAddressCommand  = new RelayCommand( args => SelectAddress(args) );

在xml中

<Button Command={Binding SelectAddressCommand}
        CommandParameter={...}

但根据我的理解,您可以通过使用ListBox 而不是ItemsControl 来省去很多麻烦。它内置了SelectedItem 属性,您可以直接将其绑定到您的视图模型

【讨论】:

  • 即使我使用中继而不是代码后面,如何从当前上下文和父上下文构造参数?
  • @MPavlak 查找 RelativeResource Self/FindAncestor
  • 即使有相对资源,这不会给我一个非此即彼的情况。我需要两个对象的东西。如果它有所作为,这是一个 uwp 应用程序,我正在尝试将此问题用作参考 stackoverflow.com/questions/32861612/…
  • @MPavlak 就像我在上一句中所说的那样。您应该使用 MVVM 设计模式,并且您想要传递给命令的所有信息都应该已经存在于 ViewModel 中,您可以使用空参数调用该命令。但如果你绝对想这样做,你可以使用 MultiBinding 将两个参数与 2 个相对资源绑定结合起来
  • 该模式不是 MVVM。那是指挥。我正在使用 MVVM,我没有理由无法访问数据的父上下文。使用所有参数加载按钮遵循我个人不关心的命令模式,因为它使按钮了解父模型。
猜你喜欢
  • 2011-01-27
  • 1970-01-01
  • 2015-08-30
  • 2018-11-14
  • 1970-01-01
  • 2018-05-14
  • 2010-12-11
  • 2011-05-11
  • 2013-11-05
相关资源
最近更新 更多