WPF ListBox DataTemplate MenuItem MVVM
//xaml <Window x:Class="WpfApp212.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:WpfApp212" mc:Ignorable="d" WindowState="Maximized" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <Style TargetType="{x:Type TextBlock}" x:Key="tbStyle"> <Setter Property="FontSize" Value="20"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="FontSize" Value="40"/> <Setter Property="Foreground" Value="Blue"/> </Trigger> </Style.Triggers> </Style> <Style TargetType="ListBoxItem"> <Setter Property="FontSize" Value="30"/> <Setter Property="Foreground" Value="Black"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="FontSize" Value="80"/> <Setter Property="Foreground" Value="Blue"/> <Setter Property="FontWeight" Value="ExtraBold"/> </Trigger> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontSize" Value="100"/> <Setter Property="Foreground" Value="Red"/> </Trigger> </Style.Triggers> </Style> <Style TargetType="{x:Type MenuItem}"> <Setter Property="FontSize" Value="50"/> </Style> </Window.Resources> <Grid> <ListBox ItemsSource="{Binding PersonsList}" Grid.IsSharedSizeScope="True"> <ListBox.ContextMenu> <ContextMenu> <MenuItem Header="Save Selected As Json" Command="{Binding SaveAsJsonCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu},Path=PlacementTarget}"/> <MenuItem Header="Save All As Json" Command="{Binding SaveAllAsJsonCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu},Path=PlacementTarget}"/> </ContextMenu> </ListBox.ContextMenu> <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" SharedSizeGroup="abc"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Name}" Margin="4"/> <TextBlock Grid.Column="1" Text="{Binding Age,StringFormat=is {0} years old}" VerticalAlignment="Bottom" Margin="4"/> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window> //cs using Microsoft.Win32; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp212 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var vm = new BookVM(); this.DataContext = vm; } } public class BookVM { public BookVM() { InitData(); } private void InitData() { PersonsList = new ObservableCollection<Person>(); for (int i = 0; i < 10000000; i++) { PersonsList.Add(new Person() { Age = i + 1, Name = $"Name_{i + 1}" }); } } public ObservableCollection<Person> PersonsList { get; set; } private DelCmd saveAsJsonCmd; public DelCmd SaveAsJsonCmd { get { if (saveAsJsonCmd == null) { saveAsJsonCmd = new DelCmd(SaveAsJsonCmdExecuted, SaveAsJsonCmdCanExecute); } return saveAsJsonCmd; } } private bool SaveAsJsonCmdCanExecute(object obj) { return true; } private void SaveAsJsonCmdExecuted(object obj) { var lbx = obj as ListBox; if (lbx != null) { string selectedItemJson = JsonConvert.SerializeObject(lbx.SelectedItem, Formatting.Indented); SaveFileDialog dialog = new SaveFileDialog(); dialog.Filter = "Json Files|*.json|All Files|*.*"; if (dialog.ShowDialog() == true) { File.AppendAllText(dialog.FileName, selectedItemJson); } } } private DelCmd saveAllAsJsonCmd; public DelCmd SaveAllAsJsonCmd { get { if (saveAllAsJsonCmd == null) { saveAllAsJsonCmd = new DelCmd(SaveAllAsJsonCmdExecuted, SaveAllAsJsonCmdCanExecute); } return saveAllAsJsonCmd; } } private bool SaveAllAsJsonCmdCanExecute(object obj) { return obj != null && (obj is ListBox); } private void SaveAllAsJsonCmdExecuted(object obj) { var lbx = obj as ListBox; if (lbx != null) { var allItemsJson = JsonConvert.SerializeObject(lbx.Items, Formatting.Indented); SaveFileDialog dilaog = new SaveFileDialog(); dilaog.Filter = "Json Files|*.json|All Files|*.json"; if (dilaog.ShowDialog() == true) { File.AppendAllText(dilaog.FileName, allItemsJson); } } } } public class DelCmd : ICommand { public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } private readonly Action<object> execute; private readonly Predicate<object> canExecute; public DelCmd(Action<object> executeValue,Predicate<object> canExecuteValue) { execute = executeValue; canExecute = canExecuteValue; } public DelCmd(Action<object> executeValue):this(executeValue,null) { } public bool CanExecute(object parameter) { if(canExecute==null) { return true; } return canExecute(parameter); } public void Execute(object parameter) { execute(parameter); } } public class Person { public string Name { get; set; } public int Age { get; set; } } }