Как связать CommandParameter с дочерним элементом дочернего элемента UserControl? - PullRequest
1 голос
/ 19 сентября 2019

У меня есть следующее представление 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 в моем MyViews.PersonView xaml выше, как мне передать это PersonCardView моей команде?Другими словами, как мне исправить

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

, чтобы он работал?

В настоящее время единственным решением, которое я нашел для этой проблемы, является замена Command и CommandParameter на

ItemClick="OnPrintBtnClick"

, а затем в коде PersonViewсделать файл:

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

Это работает, но я не могу поверить, что другого пути нет.Я не доволен этим решением, потому что у меня больше нет преимуществ от использования Command, например, от автоматической оценки метода Command CanExecute.Я также мог бы поместить xaml-код CardView в PersonView.xaml, но мне бы хотелось, чтобы мои элементы управления были в отдельных файлах, потому что у меня такое ощущение, что он более структурирован, и у каждого пользовательского элемента управления есть свои обязанности, которые можно разделить на отдельныефайлы.Кроме того, это решение слишком тесно связывает мой взгляд с моделью моего вида.

Может кто-нибудь помочь мне, пожалуйста?

Ответы [ 2 ]

1 голос
/ 19 сентября 2019

Не меняя существующую иерархию представления и модели представления, я смог передать GridControl.View в PersonViewModel с помощью свойства Tag . Вы можете назначить CardView свойству Tag в нижней части вашего CardView UserControl.и затем получите доступ к этому тегу как CommandParameter.

CardView UserControl

<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}"
                   />
0 голосов
/ 19 сентября 2019

Основываясь на ценном вкладе 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);
  }
}

Поскольку я хочу выделить кнопку «Печать» серым цветом, когда нет выбораМне все еще нужно добавить код, чтобы это произошло.Я могу получить его, обновив код кнопки до

<dxb:BarButtonItem x:Name="bbiPrint"
  Content="{Binding Print, Source={StaticResource CommonResources}}" 
  ItemClick="OnPrintBtnClick" IsEnabled="{Binding CanPrintPersons}"/> 
обновление свойства CanPrintPersons в изменении выбора PersonViewModel при выборе Персоны

Вот и все.

Решение CardViewModel

В этом решении у нас есть PersonView с его базовым PersonViewModel и CardView с его базовым CardViewModel.Я не буду описывать это решение со всеми подробностями, поскольку в моей ситуации это излишне, но для полноты изложения я приведу основные моменты.После нажатия кнопки Print на PersonView вызывается PersonViewModel 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>

, потому что у меня два ребенкавиды, которые я хочу распечатать, мне нужно добавить модель вида для каждого из них.Кроме того, эти две модели представления плюс PersonViewModel нуждаются в доступе к списку лиц для печати.В частности, им нужен общий доступ к одним и тем же данным, чтобы они были синхронизированы.Простой способ сделать это объяснен здесь и полностью выполним.Но я думаю, что это не стоит проблем для простого варианта использования, который у меня есть, поскольку он добавляет больше сложности, чем необходимо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...