0

i have a TableModel which looks like this:

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));
            }
        }
    }
}

then view model

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));
            }
        }
    }
}

and the view:

<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>

and the MainWindow.xaml.cs, where i set the datacontext:

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();           
        }
    }
}

The items control displays all data like i want, but i don't know, how to change/delete the selected player with the context menu.

How can i get the right datacontext (Table and the property Players) to edit or delete a player?

Does someone have a litte thought in the right direction for me?

Thanks in advance, best regards,

Flo

FloSu
  • 15
  • 5
  • Bind to a command in your View, `Command="{Binding DeleteCommand}"`. In your `ViewModel` delete the selected item from your list. Look at the ViewModel part of this answer https://stackoverflow.com/a/40101345/2289942 – Nawed Nabi Zada Oct 10 '19 at 05:59
  • Thanks for your answer. i implemented this like written in the post, but if i click the context-menu, i get an data-error, i think, i have the wrong datacontext, but in this case, i dont know, how to changen it ... System.Windows.Data Error: 40 : BindingExpression path error: 'MyCommand' property not found on 'object' ''PokerTable' (HashCode=2679067)'. BindingExpression:Path=MyCommand; DataItem='Table' (HashCode=2679067); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand') – FloSu Oct 10 '19 at 06:30
  • I'm so sorry, but this also won't work, it seems like the binding don't find the Usercontrol and i don't know why: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=DataContext.MyCommand; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand') – FloSu Oct 10 '19 at 07:22
  • Thx a lot for your answers, but the error is still there ... Maybe its becaue of the contextmenu, which is not in the visual tree? – FloSu Oct 10 '19 at 08:39
  • I've edited the question and added the desired information. Thank you so much! – FloSu Oct 10 '19 at 09:21
  • Now the whole code is copy & pasted, sorry for that – FloSu Oct 10 '19 at 10:27

1 Answers1

0

I tried your code.

Indeed since the ContextMenu is not on the same VisualTree FindAncestor will not work.

But this will work:

<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>

EDIT:

I am not sure what the requirements are but, without modifying your code too much and to give you an understanding of what you can do:

  1. Create a ViewModel for each Table and keep the properties like SelectedPlayer etc on that VM, and of course Commands as well.

  2. Create a TableId property in Player, so you know which Table a player is playing on. This means a Player can only play at one Table at a time.

Sample code for the second approach:

Player class:

public class Player : INotifyPropertyChanged
{
   private int? _playingOnTableId;

   public int? PlayingOnTableId
   {
       get => _playingOnTableId;
       set
       {
          if(_playingOnTableId == value) return;
          _playingOnTableId = value;
          OnPropertyChanged(nameof(PlayingOnTableId);
       }    
   }
}

Table class:

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
    } 
}

NOTE: You need to give your Table unique ID!

From your TableViewModel you can use these method to Add and Remove players:

Add:

TableModel1.AddPlayer(new Player { ID = 1, Name = "Tim", State = Player.StateEnum.Normal });

Remove:

Tables.FirstOrDefault(t => TableId == SelectedPlayer.PlayingOnTableId)?.DeletePlayer(SelectedPlayer);

That's the basics, you have to make sure player is removed from other tables if they are part of those.

Nawed Nabi Zada
  • 2,819
  • 5
  • 29
  • 40
  • Now i have the correct datacontext, thank you so much! One last question: With SelectedPlayer.State i can set the state of the selected player, that work fine. But how can i delete the player from the list? I only know the selected player, not the object "TableModel", where the property "Players" exist. How can i achieve the Observablecollection "Players", where i can perform the removal? – FloSu Oct 10 '19 at 12:25
  • @FloSu Well... Basically your approach is wrong, You simply don't know which table player belongs to. I would set the `SelectedPlayer` per table. And removing would be `Players.Remove(SelectedPlayer);` – Nawed Nabi Zada Oct 10 '19 at 12:42
  • I've edited the code above once again, the SelectedPlayer is now binded to the property SelectedPlayer of the table, also there is a new void to remove the player. But how can i tell MyCommand to execute the void RemovePlayer of the table model? I'm a little bit confused. – FloSu Oct 11 '19 at 04:56
  • Thanks, now it works like a charm. i don't know, if its the best way to do so, all i want is add some tables to a list and display the tables with the players. In the view, i want to add/delete tables, and edit the players (add/remove/rename/set new state). Is there a better way to do so? – FloSu Oct 11 '19 at 10:56