Как отобразить список разных объектов (дочерних) в WPF и привязать кнопку внутри списка к каждой строке?

#c# #wpf #xaml #mvvm

#c# #wpf #xaml #mvvm

Вопрос:

Как отобразить список разных объектов (дочерних) в WPF и привязать кнопку внутри списка к каждой строке? У меня есть 4 класса: Person (родитель), Student (дочерний элемент Person), Staff (дочерний элемент Person), Teacher (дочерний элемент Staff)

В представлении:

 <ListView Grid.Row="0"  Name="List" HorizontalContentAlignment="Center" ItemsSource="{Binding Path=PeopleList}" SelectedItem="{Binding RowSelectedItem, UpdateSourceTrigger=PropertyChanged}" SelectionMode="Single">
        <ListView.View>
            <GridView>
                <GridView.Columns>
                    <GridViewColumn Width="180" x:Name="Column_Type" 
                                    DisplayMemberBinding="{Binding Type}" />
                    <GridViewColumn Width="180"
                                    DisplayMemberBinding="{Binding FullName}" />
                    <GridViewColumn Width="180">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <dx:SimpleButton Content="SELECT" Width="180" Command="{Binding DataContext.SelectCommand, ElementName=Column_Type}" CommandParameter="{Binding ElementName=Column_Type, Path=Type}"/>

                                </StackPanel>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView.Columns>
            </GridView>
        </ListView.View>
    </ListView>
  

в ViewModel:

     #region Select

    DelegateCommand<Person> selectCommand;
    public DelegateCommand<Person> SelectCommand
    {
        get => selectCommand ??
               (selectCommand = new DelegateCommand<Person>(Select, CanSelect));
    }
    public bool CanSelect(Person rowItemType)
    {
        if (rowItemType.Type == "Teacher" || rowItemType.Type == "Student")
            return true;
        else
            return false;
    }
    public void Select(Person selectedItem)
    {
     //if(RowSelectedItem.Type == "Staff")
     //   RowSelectedItem.
        //Refresh();
    }

    #endregion
  

Комментарии:

1. Вы можете создать DelegateCommand в каждой из ViewModels для разных объектов (Person, Student и т.д.) и вместо DataContext.SelectCommand разметки просто поставить SelectCommand . В ElementName=Column_Type этом случае нет необходимости.

2. Вы привязываете Person.Type как параметр команды, но ваши методы execute и CanExecute имеют Person параметр as

Ответ №1:

Вы можете привязать свойство IsEnabled и использовать конвертер,

в XAML:

 <Button Content="SELECT"
    Width="180"
    IsEnabled="{Binding ., Converter={StaticResource TypeConverter}}" />
  

и новый конвертер:

 public class TypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var isPerson = value is Person;
        return isPerson;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
  

Ответ №2:

Это очень упрощенный ответ, ваш сценарий, скорее всего, будет намного сложнее, но он иллюстрирует то, что я написал в комментарии выше.

MainWindow.xaml

 <Window x:Class="TestWpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Grid>
        <ListView HorizontalContentAlignment="Center" ItemsSource="{Binding Path=PeopleList}" SelectionMode="Single">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Width="180" DisplayMemberBinding="{Binding Type}" />
                        <GridViewColumn Width="180" DisplayMemberBinding="{Binding FullName}" />
                        <GridViewColumn Width="180">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Button Content="SELECT" Width="180" Command="{Binding SelectCommand}" />
                                    </StackPanel>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>
  

MainWindow.xaml.cs

 using System.Collections.ObjectModel;
using System.Windows;

namespace TestWpfApplication
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        public Collection<object> PeopleList => new Collection<object>
        {
            new Person(),
            new Student(),
            new Person(),
            new Student()
        };
    }
}
  

Person.cs

 using System;
using Prism.Commands;

namespace TestWpfApplication
{
    internal class Person
    {
        DelegateCommand selectCommand;

        public string Type => "Person";

        public string FullName => "My name is person";

        public DelegateCommand SelectCommand
        {
            get => selectCommand ?? (selectCommand = new DelegateCommand(Select, CanSelect));
        }
        public bool CanSelect()
        {
            return true;
        }

        public void Select()
        {
            Console.WriteLine("Person Clicked");
        }
    }
}
  

Student.cs

 using System;
using Prism.Commands;

namespace TestWpfApplication
{
    internal class Student
    {
        DelegateCommand selectCommand;

        public string Type => "Student";

        public string FullName => "My name is student";

        public DelegateCommand SelectCommand
        {
            get => selectCommand ?? (selectCommand = new DelegateCommand(Select, CanSelect));
        }

        public bool CanSelect()
        {
            return false;
        }

        public void Select()
        {
            Console.WriteLine("Student Clicked");
        }
    }
}
  

Если вы хотите, чтобы какая-то логика выполнялась в MainWindow.xaml.cs, вы можете создать событие в Person.cs amp; Student.cs и «подключить» их, когда объекты будут созданы и добавлены в PeopleList коллекцию.

Комментарии:

1. Спасибо тебе за твой ответ, брат; но поскольку я делаю с шаблоном MVVM, я хотел бы поместить всю логику в ViewModel. итак, я должен отправить параметр из View.xaml с помощью SelectCommand типа «Type» в ViewModel.cs; итак, вы знаете, как отправить параметр «Type» (который является свойством в Person.cs (parent)) с кнопки в View.xaml в SelectCommand в ViewModel.cs, чтобы распознать тип пользователя в каждой строке?

2. То же самое применимо, логика в моем примере для MainWindow.xaml.cs просто становится логикой для MainViewModel.cs для представления. Таким образом, Person.cs, Student.cs и т.д. … Становятся дочерними ViewModels или просто моделями в этом случае, и они могут обрабатывать событие кнопки и event вплоть до MainViewModel, если это необходимо. Я не хотел чрезмерно усложнять пример.

3. Спасибо за ваш быстрый ответ; но я не хочу писать три SelectCommand отдельно для каждого класса. на самом деле, я хочу написать только одну SelectCommand в ViewModel и по одному свойству в качестве параметра для распознавания типа