如何将CommandParameter与子UserControl的子元素绑定?

时间:2019-09-19 07:48:45

标签: c# wpf xaml prism

我有以下xaml视图:

<UserControl x:Class="MyViews.PersonView"
  xmlns:views="clr-namespace:MyViews"
 [...]
>
[...]
<dxb:BarManager x:Name="MainBarManager">
  <dxb:BarManager.Items>
    <dxb:BarButtonItem x:Name="bbiPrint"
                       Content="{Binding Print, Source={StaticResource CommonResources}}" 
                       Command="{Binding PrintPersonsCommand}" 
                       CommandParameter="{Binding PersonsCardView, ElementName=CardUserControl}"
                       />
  </dxb:BarManager.Items>
  <Grid>
    <Grid.RowDefinitions>
    [...]
    </Grid.RowDefinitions>
    <views:CardView x:Name="CardUserControl" Grid.Row="2"/>
  </Grid>
[...]
</UserControl>

CardView的定义如下:

<UserControl x:Class="MyViews.CardView"
             [...]>
[...]

    <dxg:GridControl ItemsSource="{Binding Persons}" SelectedItems="{Binding SelectedPersons}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionMode="MultipleRow">
        [...]
        <dxg:GridControl.View>
            <dxg:CardView x:Name="PersonsCardView" 
                          [...]
                          CardTemplate="{StaticResource DisplayCardTemplate}" 
                          PrintCardViewItemTemplate="{StaticResource PrintCardTemplate}"/>
        </dxg:GridControl.View>
        [...]
    </dxg:GridControl>
</UserControl>

PrintPersonsCommand在我的ViewModel中定义如下:

public class PersonViewModel
{
  public PersonViewModel(...)
  {
    [...]
    PrintPersonsCommand = new Prism.Commands.DelegateCommand<DataViewBase>(PrintPersons, CanPrintPersons);
  }  

  public Prism.Commands.DelegateCommand<DataViewBase> PrintPersonsCommand { get; private set; }

  private void PrintPersons(DataViewBase view)
  {
    _printService.ShowGridViewPrintPreview(view);
  }

  private bool CanPrintPersons(DataViewBase view)
  {
    return true;
  }
}

现在,当我单击“打印”按钮时,上面的PrintPersons方法总是带有null。如何在上面的CardUserControl.PersonsCardView xaml中传递MyViews.PersonView,如何将PersonCardView传递给命令?换句话说,我该如何解决

CommandParameter="{Binding PersonsCardView, ElementName=CardUserControl}"

使其起作用?

当前,我发现的唯一解决方案是将

替换为CommandCommandParameter
ItemClick="OnPrintBtnClick"

,然后在PersonView的代码隐藏文件中执行以下操作:

private void OnPrintBtnClick(object sender, ItemClickEventArgs e)
{
  var ctxt = DataContext as PersonViewModel;
  ctxt.PrintPersonsCommand.Execute(CardUserControl.PersonsCardView);
}

那行得通,但是我不敢相信没有其他办法。我对该解决方案不满意,因为我再也没有使用Command的好处,例如自动评估Command的CanExecute方法。我也可以将CardView的xaml代码放在PersonView.xaml中,但是我希望控件可以放在单独的文件中,因为我感觉它的结构更加合理,每个用户控件都有自己的职责,可以很好地完成任务被分割成单独的文件。而且,该解决方案过分紧密地将我的视图绑定到我的视图模型。

有人可以帮我吗?

2 个答案:

答案 0 :(得分:2)

在不更改现有视图和视图模型层次结构的情况下,我能够使用 Tag 属性将GridControl.View传递给PersonViewModel。 您可以将CardView分配给CardView UserControl底部的Tag属性,然后以CommandParameter的身份访问此Tag。

CardView用户控件

<UserControl x:Class="MyViews.CardView"
         [...]>
    [...]

<dxg:GridControl ItemsSource="{Binding Persons}" SelectedItems="{Binding SelectedPersons}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SelectionMode="MultipleRow">
    [...]
    <dxg:GridControl.View>
        <dxg:CardView x:Name="PersonsCardView" 
                      [...]
                      CardTemplate="{StaticResource DisplayCardTemplate}" 
                      PrintCardViewItemTemplate="{StaticResource PrintCardTemplate}"/>
    </dxg:GridControl.View>
    [...]
</dxg:GridControl>

<UserControl.Tag>
    <Binding ElementName="PersonsCardView"/>
</UserControl.Tag>

</UserControl>

打印按钮Xaml:

<dxb:BarButtonItem x:Name="bbiPrint"
                   Content="{Binding Print, Source={StaticResource CommonResources}}" 
                   Command="{Binding PrintPersonsCommand}" 
                   CommandParameter="{Binding ElementName=CardUserControl, Path=Tag}"
                   />

答案 1 :(得分:0)

根据Insane的宝贵意见,我提出了以下两个更清洁的修复程序:

代码隐藏解决方案

PersonView中,使用ItemClick按钮上的Print事件处理程序:

<dxb:BarButtonItem x:Name="bbiPrint"
  Content="{Binding Print, Source={StaticResource CommonResources}}" 
  ItemClick="OnPrintBtnClick"/>

像这样修改相应的代码隐藏文件:

public partial class PersonView : UserControl
{
  readonly IPrintService _printService;

  public PersonView(IPrintService printService)
  {
    _printService = printService;

    InitializeComponent();
  }

  private void OnPrintBtnClick(object sender, ItemClickEventArgs e)
  {
    _printService.ShowGridViewPrintPreview(CardUserControl.PersonsCardView);
  }
}

因为在没有选择的情况下我想将“打印”按钮涂成灰色,所以我仍然需要添加一些代码来实现。我可以得到 1.将按钮代码更新为

<dxb:BarButtonItem x:Name="bbiPrint"
  Content="{Binding Print, Source={StaticResource CommonResources}}" 
  ItemClick="OnPrintBtnClick" IsEnabled="{Binding CanPrintPersons}"/> 
  1. 在“人物”选择更改后刷新CanPrintPersons中的PersonViewModel属性

就是这样。

CardViewModel解决方案

在该解决方案中,我们有一个PersonView及其底层PersonViewModel和一个CardView及其底层CardViewModel。我不会详细描述该解决方案,因为在我的情况下这是过分的,但是为了完整起见,我将提出要点。单击Print上的PersonView按钮后,将调用PersonViewModel的{​​{1}}。该命令向PrintCommand发出Print事件,该事件依次调用其自己的CardViewModel。后一个命令调用

PrintCommand

其中_printService.ShowGridViewPrintPreview(View); View的属性,该属性是在CardViewModel加载时设置的,例如

CardView

因为我要打印两个子视图,所以我需要为每个视图添加一个视图模型。此外,这两个视图模型加上<dxmvvm:Interaction.Behaviors> <dxmvvm:EventToCommand EventName="Loaded" Command="{Binding ViewLoadedCommand}" CommandParameter="{Binding ElementName=PersonsCardView}" /> </dxmvvm:Interaction.Behaviors> 需要访问要打印的人员列表。特别是,它们需要对相同数据的共享访问权,以便它们被同步。 here说明了一种简单的方法,并且完全可行。但是我认为,对于我拥有的简单用例而言,这不值得麻烦,因为它增加了不必要的复杂性。