#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 и по одному свойству в качестве параметра для распознавания типа