wpf - WPF MVVM:从项目控件中的列表框中删除项目
问题描述
我有一个看起来像这样的 TableModel:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
namespace TestPRL.Model
{
public class TableModel : INotifyPropertyChanged
{
public TableModel()
{
_Players = new ObservableCollection<Player>();
}
private string _Name;
public string Name
{
get { return _Name; }
set {
if (_Name!=value)
{
_Name = value;
RaisePropertyChanged("Name");
}
}
}
private string _Game;
public string Game
{
get { return _Game; }
set
{
if (_Game != value)
{
_Game = value;
RaisePropertyChanged("Game");
}
}
}
private ObservableCollection<Player> _Players;
public ObservableCollection<Player> Players
{
get { return _Players; }
set {
if (_Players!=value)
{
_Players = value;
RaisePropertyChanged("Players");
}
}
}
public void DeletePlayer()
{
_Players.Remove(_SelectedPlayer);
RaisePropertyChanged("Players");
}
private Player _SelectedPlayer;
public Player SelectedPlayer
{
get
{
return _SelectedPlayer;
}
set
{
if (_SelectedPlayer != value)
{
_SelectedPlayer = value;
Trace.WriteLine(_SelectedPlayer.Name);
RaisePropertyChanged("SelectedPlayer");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class Player : INotifyPropertyChanged
{
public enum StateEnum
{
Normal,
Waiting,
Playing,
Absent
}
private int _ID;
public int ID
{
get { return _ID; }
set
{
if (_ID != value)
{
_ID = value;
RaisePropertyChanged("ID");
}
}
}
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (_Name != value)
{
_Name = value;
RaisePropertyChanged("Name");
}
}
}
private StateEnum _State;
public StateEnum State
{
get { return _State; }
set
{
if (_State != value)
{
_State = value;
RaisePropertyChanged("State");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
然后查看模型
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using TestPRL.Model;
using TestPRL.Classes;
using TestPRL.View;
namespace TestPRL.ViewModel
{
public class TableViewModel:INotifyPropertyChanged
{
public TableViewModel()
{
MyCommand = new RelayCommand(new Action<object>(MyCommand_Execute));
Tables = new ObservableCollection<TableModel>();
}
public ObservableCollection<TableModel> Tables { get; set; }
public void LoadTables()
{
TableModel TableModel1 = new TableModel();
TableModel1.Name = "1";
TableModel1.Game = "Game1";
TableModel1.Players.Add(new Player { ID = 1, Name = "Tim", State = Player.StateEnum.Normal });
TableModel1.Players.Add(new Player { ID = 2, Name = "Tom", State = Player.StateEnum.Absent });
TableModel1.Players.Add(new Player { ID = 3, Name = "Reese", State = Player.StateEnum.Playing });
TableModel1.Players.Add(new Player { ID = 4, Name = "Paula", State = Player.StateEnum.Normal });
TableModel TableModel2 = new TableModel();
TableModel2.Name = "2";
TableModel2.Game = "Game2";
TableModel2.Players.Add(new Player { ID = 1, Name = "Karen", State = Player.StateEnum.Normal });
TableModel2.Players.Add(new Player { ID = 2, Name = "Summer", State = Player.StateEnum.Playing });
TableModel2.Players.Add(new Player { ID = 3, Name = "Willy", State = Player.StateEnum.Absent });
TableModel2.Players.Add(new Player { ID = 4, Name = "Mike", State = Player.StateEnum.Waiting });
TableModel2.Players.Add(new Player { ID = 4, Name = "Peter", State = Player.StateEnum.Waiting });
Tables.Add(TableModel1);
Tables.Add(TableModel2);
}
private ICommand _MyCommand;
public ICommand MyCommand
{
get { return _MyCommand; }
set { _MyCommand = value; }
}
private void MyCommand_Execute(object sender)
{
var myView = sender as TableView;
myView?.Test();
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
和观点:
<UserControl x:Class="TestPRL.View.TableView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:rootns="clr-namespace:TestPRL"
xmlns:local="clr-namespace:TestPRL.View"
xmlns:classes="clr-namespace:TestPRL.Classes"
xmlns:viewmodel="clr-namespace:TestPRL.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Name="TablesGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Left">
<ItemsControl ItemsSource="{Binding Tables}" x:Name="MyTables">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="0" Margin="5">
<StackPanel Orientation="Vertical" Margin="6">
<Label Content="{Binding Name}" Foreground="Black" FontSize="16" FontWeight="Bold" Background="Transparent" BorderThickness="0"/>
<ListBox Name="PlayerListbox" ItemsSource="{Binding Players}" SelectedItem="{Binding DataContext.SelectedPlayer, RelativeSource={RelativeSource AncestorType=UserControl}}" IsSynchronizedWithCurrentItem="True" Background="#33A2A2A2" Foreground="White" BorderThickness="0" FontWeight="Bold" FontSize="12" Margin="0,15,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name = "TextControl" Text="{Binding Name}" Background="Transparent" Padding="4">
<TextBlock.Style>
<Style TargetType = "TextBlock" >
<Style.Triggers >
<DataTrigger Binding="{Binding State}" Value="Playing">
<Setter Property = "Foreground" Value="Blue"/>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="Waiting">
<Setter Property = "Foreground" Value="Gray"/>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="Absent">
<Setter Property = "Foreground" Value="#FF0097FF"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding Path=DataContext, Source={x:Reference TablesGrid}}">
<MenuItem Header="Delete player" Command="{Binding MyCommand}" CommandParameter="{Binding}"/>
<MenuItem Header="Set player state to playing"/>
<MenuItem Header="Set player state to absent"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</UserControl>
MainWindow.xaml:
<Window x:Class="TestPRL.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"
xmlns:local="clr-namespace:TestPRL"
xmlns:view = "clr-namespace:TestPRL.View"
xmlns:viewmodel ="clr-namespace:TestPRL.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
</Window.Resources>
<Grid>
<view:TableView x:Name="TableViewControl" Loaded="TableViewControl_Loaded"/>
</Grid>
</Window>
和 MainWindow.xaml.cs,我在其中设置数据上下文:
using System.Windows;
namespace TestPRL
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TableViewControl_Loaded(object sender, RoutedEventArgs e)
{
TestPRL.ViewModel.TableViewModel TableViewModel = new ViewModel.TableViewModel();
TableViewControl.DataContext = TableViewModel;
TableViewModel.LoadTables();
}
}
}
项目控件显示我想要的所有数据,但我不知道如何使用上下文菜单更改/删除选定的播放器。
如何获得正确的数据上下文(表和属性 Players)来编辑或删除播放器?
有人对我有正确的想法吗?
预先感谢,最好的问候,
弗洛
解决方案
我试过你的代码。
确实,由于ContextMenu
不在同一位置,因此VisualTree
FindAncestor
将无法正常工作。
但这会起作用:
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding Path=DataContext, Source={x:Reference TablesGrid}}">
<MenuItem Header="Delete player" Command="{Binding Path=MyCommand}"/>
<MenuItem Header="Set player state to playing"/>
<MenuItem Header="Set player state to absent"/>
</ContextMenu>
</ListBox.ContextMenu>
编辑:
我不确定要求是什么,但是,无需过多地修改您的代码并让您了解您可以做什么:
ViewModel
为每个创建一个并保留诸如etcTable
之类的属性,当然也是如此。SelectedPlayer
VM
Commands
在 中创建一个
TableId
属性Player
,这样您就知道玩家正在哪个表上玩。这意味着一次Player
只能玩一个Table
。
第二种方法的示例代码:
球员等级:
public class Player : INotifyPropertyChanged
{
private int? _playingOnTableId;
public int? PlayingOnTableId
{
get => _playingOnTableId;
set
{
if(_playingOnTableId == value) return;
_playingOnTableId = value;
OnPropertyChanged(nameof(PlayingOnTableId);
}
}
}
表类:
public class TableModel : INotifyPropertyChanged
{
private int? _tableId;
public int? TableId
{
get => _tableId;
set
{
if(_tableId == value) return;
_tableId = value;
OnPropertyChanged(nameof(TableId);
}
}
public void DeletePlayer(Player playerToDelete)
{
Players.Remove(playerToDelete);
playerToDelete.PlayingOnTableId = null;
}
public void AddPlayer(Player playerToDelete)
{
Players.Add(playerToDelete);
playerToDelete.PlayingOnTableId = TableId;
//YES, You need to give your tables unique ID
}
}
注意:您需要为您的表提供唯一 ID!
从您的 TableViewModel 中,您可以使用这些方法来添加和删除玩家:
添加:
TableModel1.AddPlayer(new Player { ID = 1, Name = "Tim", State = Player.StateEnum.Normal });
消除:
Tables.FirstOrDefault(t => TableId == SelectedPlayer.PlayingOnTableId)?.DeletePlayer(SelectedPlayer);
这就是基础,如果玩家是其中的一部分,您必须确保将其从其他牌桌中移除。
推荐阅读
- java - 结果未使用聚合和 MongoDB 排序
- sql - 是否有一个 SQL 函数每 3 行重新开始行计数?
- node.js - 如何使用 node.js 在 mongodb 中使用特定字段更新用户信息
- java - 是否可以创建将内容保存在内存中而不是内容存储库中的自定义 Apache NiFi 处理器?
- javafx - JavaFx:检查注册中的用户名和密码是否与登录匹配
- python - 这是一个逻辑错误还是奇怪的统计行为?
- netsuite - SuiteScript 2.0 在客户端脚本中刷新子列表的方法
- powerbi - DAX 查找最大总和并返回相关值
- reactjs - 内联 InputLabelProps 三元组
- javascript - 展平可填充 PDF