首页 > 解决方案 > 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)来编辑或删除播放器?

有人对我有正确的想法吗?

预先感谢,最好的问候,

弗洛

标签: wpfbinding

解决方案


我试过你的代码。

确实,由于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>

编辑:

我不确定要求是什么,但是,无需过多地修改您的代码并让您了解您可以做什么:

  1. ViewModel为每个创建一个并保留诸如etcTable之类的属性,当然也是如此。SelectedPlayerVMCommands

  2. 在 中创建一个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);

这就是基础,如果玩家是其中的一部分,您必须确保将其从其他牌桌中移除。


推荐阅读