






















Install-Package Microsoft.Xaml.Behaviors.Wpf
public class GridBehavior : Behavior<Grid> { private ContextMenu contextMenu; public GridBehavior() { contextMenu = new ContextMenu(); MenuItem saveMenu = new MenuItem(); saveMenu.Header = "Save Selected Items"; saveMenu.Click += SaveMenu_Click; contextMenu.Items.Add(saveMenu); } private void SaveMenu_Click(object sender, RoutedEventArgs e) { if (SaveSelectedItemsCommand != null && SaveSelectedItemsCommand.CanExecute(SaveCommandParameter)) { SaveSelectedItemsCommand?.Execute(SaveCommandParameter); } } public ICommand SaveSelectedItemsCommand { get { return (ICommand)GetValue(SaveSelectedItemsCommandProperty); } set { SetValue(SaveSelectedItemsCommandProperty, value); } } // Using a DependencyProperty as the backing store for SaveSelectedItemsCommand. This enables animation, styling, binding, etc... public static readonly DependencyProperty SaveSelectedItemsCommandProperty = DependencyProperty.Register("SaveSelectedItemsCommand", typeof(ICommand), typeof(GridBehavior), new PropertyMetadata(null)); public object SaveCommandParameter { get { return (object)GetValue(SaveCommandParameterProperty); } set { SetValue(SaveCommandParameterProperty, value); } } // Using a DependencyProperty as the backing store for SaveCommandParameter. This enables animation, styling, binding, etc... public static readonly DependencyProperty SaveCommandParameterProperty = DependencyProperty.Register("SaveCommandParameter", typeof(object), typeof(GridBehavior), new PropertyMetadata(null)); protected override void OnAttached() { base.OnAttached(); AssociatedObject.ContextMenu = contextMenu; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.ContextMenu = null; } }
<ItemsControl ItemsSource="{Binding BooksCollection}"> <ItemsControl.Template> <ControlTemplate> <ScrollViewer CanContentScroll="True"> <VirtualizingStackPanel IsItemsHost="True" ScrollViewer.CanContentScroll="True" ScrollViewer.IsDeferredScrollingEnabled="True" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.CacheLength="2,2" VirtualizingPanel.CacheLengthUnit="Item" UseLayoutRounding="True" SnapsToDevicePixels="True" /> </ScrollViewer> </ControlTemplate> </ItemsControl.Template> <ItemsControl.ItemTemplate> <DataTemplate> <Grid Width="{x:Static SystemParameters.FullPrimaryScreenWidth}" Height="{x:Static SystemParameters.FullPrimaryScreenHeight}"> <!--<behavior:Interaction.Triggers> <behavior:EventTrigger EventName="MouseLeftButtonUp"> <behavior:InvokeCommandAction Command="{Binding DataContext.SelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}"/> </behavior:EventTrigger> </behavior:Interaction.Triggers>--> <behavior:Interaction.Behaviors> <local:GridBehavior SaveSelectedItemsCommand="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" SaveCommandParameter="{Binding}"/> </behavior:Interaction.Behaviors> <Image Source="{Binding ImgUrl,Converter={StaticResource ImgUrlConverter}}" Stretch="Fill"/> <TextBlock Text="{Binding Id}"> <TextBlock.Style> <Style TargetType="TextBlock"> <Setter Property="FontSize" Value="50"/> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="FontSize" Value="100"/> <Setter Property="Foreground" Value="Red"/> </Trigger> </Style.Triggers> </Style> </TextBlock.Style> </TextBlock> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
<Window x:Class="WpfApp14.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:behavior="http://schemas.microsoft.com/xaml/behaviors" xmlns:local="clr-namespace:WpfApp14" mc:Ignorable="d" Title="{Binding MainTitle}" WindowState="Maximized"> <Window.DataContext> <local:MainVM/> </Window.DataContext> <Window.Resources> <local:ImgUrlConverter x:Key="ImgUrlConverter"/> </Window.Resources> <Grid> <ItemsControl ItemsSource="{Binding BooksCollection}"> <ItemsControl.Template> <ControlTemplate> <ScrollViewer CanContentScroll="True"> <VirtualizingStackPanel IsItemsHost="True" ScrollViewer.CanContentScroll="True" ScrollViewer.IsDeferredScrollingEnabled="True" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.CacheLength="2,2" VirtualizingPanel.CacheLengthUnit="Item" UseLayoutRounding="True" SnapsToDevicePixels="True" /> </ScrollViewer> </ControlTemplate> </ItemsControl.Template> <ItemsControl.ItemTemplate> <DataTemplate> <Grid Width="{x:Static SystemParameters.FullPrimaryScreenWidth}" Height="{x:Static SystemParameters.FullPrimaryScreenHeight}"> <!--<behavior:Interaction.Triggers> <behavior:EventTrigger EventName="MouseLeftButtonUp"> <behavior:InvokeCommandAction Command="{Binding DataContext.SelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}"/> </behavior:EventTrigger> </behavior:Interaction.Triggers>--> <behavior:Interaction.Behaviors> <local:GridBehavior SaveSelectedItemsCommand="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" SaveCommandParameter="{Binding}"/> </behavior:Interaction.Behaviors> <Image Source="{Binding ImgUrl,Converter={StaticResource ImgUrlConverter}}" Stretch="Fill"/> <TextBlock Text="{Binding Id}"> <TextBlock.Style> <Style TargetType="TextBlock"> <Setter Property="FontSize" Value="50"/> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="FontSize" Value="100"/> <Setter Property="Foreground" Value="Red"/> </Trigger> </Style.Triggers> </Style> </TextBlock.Style> </TextBlock> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Window> using Microsoft.Xaml.Behaviors; using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.IO; using System.Reflection.Metadata.Ecma335; using System.Runtime.CompilerServices; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; 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 WpfApp14 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } public class GridBehavior : Behavior<Grid> { private ContextMenu contextMenu; public GridBehavior() { contextMenu = new ContextMenu(); MenuItem saveMenu = new MenuItem(); saveMenu.Header = "Save Selected Items"; saveMenu.Click += SaveMenu_Click; contextMenu.Items.Add(saveMenu); } private void SaveMenu_Click(object sender, RoutedEventArgs e) { if (SaveSelectedItemsCommand != null && SaveSelectedItemsCommand.CanExecute(SaveCommandParameter)) { SaveSelectedItemsCommand?.Execute(SaveCommandParameter); } } public ICommand SaveSelectedItemsCommand { get { return (ICommand)GetValue(SaveSelectedItemsCommandProperty); } set { SetValue(SaveSelectedItemsCommandProperty, value); } } // Using a DependencyProperty as the backing store for SaveSelectedItemsCommand. This enables animation, styling, binding, etc... public static readonly DependencyProperty SaveSelectedItemsCommandProperty = DependencyProperty.Register("SaveSelectedItemsCommand", typeof(ICommand), typeof(GridBehavior), new PropertyMetadata(null)); public object SaveCommandParameter { get { return (object)GetValue(SaveCommandParameterProperty); } set { SetValue(SaveCommandParameterProperty, value); } } // Using a DependencyProperty as the backing store for SaveCommandParameter. This enables animation, styling, binding, etc... public static readonly DependencyProperty SaveCommandParameterProperty = DependencyProperty.Register("SaveCommandParameter", typeof(object), typeof(GridBehavior), new PropertyMetadata(null)); protected override void OnAttached() { base.OnAttached(); AssociatedObject.ContextMenu = contextMenu; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.ContextMenu = null; } } public class MainVM : INotifyPropertyChanged { private DateTime startTime; public MainVM() { SaveCommand = new DelCommand(SaveCommandExecuted); SelectCommand = new DelCommand(SelectCommandExecuted); Task.Run(async () => { await InitBooksCollectionAsync(); }); } private void SelectCommandExecuted(object? obj) { var bk = obj as Book; if (bk != null) { SelectedBk = bk; System.Diagnostics.Debug.WriteLine(SelectedBk.ToString()); } } private void SaveCommandExecuted(object? obj) { var bk = obj as Book; if (bk != null) { SelectedBk = bk; System.Diagnostics.Debug.WriteLine(SelectedBk.ToString()); } } private Book selectedBk; public Book SelectedBk { get { return selectedBk; } set { if (value != selectedBk) { selectedBk = value; OnPropertyChanged(); } } } public ICommand SaveCommand { get; set; } public ICommand SelectCommand { get; set; } private async Task InitBooksCollectionAsync(long cnt = 300000001) { var imgDir = @"../../../Images"; if (!Directory.Exists(imgDir)) { return; } var imgs = Directory.GetFiles(imgDir); if (imgs == null || !imgs.Any()) { return; } startTime = DateTime.Now; int imgsCount = imgs.Count(); BooksCollection = new ObservableCollection<Book>(); List<Book> booksList = new List<Book>(); for (int i = 1; i < cnt + 1; i++) { booksList.Add(new Book() { Id = i, ImgUrl = $"{imgs[i % imgsCount]}" }); if (i % 1000000 == 0) { await PopulateBooksCollectionAsync(booksList, i); } } if (booksList.Any()) { await PopulateBooksCollectionAsync(booksList, cnt); } } private async Task PopulateBooksCollectionAsync(List<Book> booksList, long i) { var tempList = booksList.ToList(); booksList.Clear(); await Application.Current.Dispatcher.InvokeAsync(() => { foreach (var bk in tempList) { BooksCollection.Add(bk); } MainTitle = $"{DateTime.Now.ToString("yyyyMMddHHmmssffff")},i:{i},loaded {BooksCollection.Count} items,{GetMem()},{GetTimeCost()}"; System.Diagnostics.Debug.WriteLine(MainTitle); }, System.Windows.Threading.DispatcherPriority.Background); } public string GetTimeCost() { return $"time cost {DateTime.Now.Subtract(startTime).TotalSeconds:N2} seconds"; } private string GetMem() { return $"memory {System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024:N2} M"; } private string mainTitle; public string MainTitle { get { return mainTitle; } set { if (value != mainTitle) { mainTitle = value; OnPropertyChanged(); } } } private ObservableCollection<Book> booksCollection; public ObservableCollection<Book> BooksCollection { get { return booksCollection; } set { if (value != booksCollection) { booksCollection = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propName = "") { var handler = PropertyChanged; handler?.Invoke(this, new PropertyChangedEventArgs(propName)); } } public static class SelectorBehavior { public static readonly DependencyProperty SelectionChangedCommandProperty = DependencyProperty.RegisterAttached("SelectionChangedCommand", typeof(ICommand), typeof(SelectorBehavior), new PropertyMetadata(null, OnCommandChanged)); public static void SetSelectionChangedCommand(DependencyObject obj, ICommand value) => obj.SetValue(SelectionChangedCommandProperty, value); public static ICommand GetSelectionChangedCommand(DependencyObject obj) => (ICommand)obj.GetValue(SelectionChangedCommandProperty); private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Selector selector) { selector.SelectionChanged -= OnSelectionChanged; if (e.NewValue != null) selector.SelectionChanged += OnSelectionChanged; } } private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e) { var command = GetSelectionChangedCommand(sender as DependencyObject); if (command != null && command.CanExecute(e)) command.Execute(e.AddedItems); } } public class DelCommand : ICommand { public event EventHandler? CanExecuteChanged; private Action<object?>? execute; private Predicate<object?>? canExecute; public DelCommand(Action<object?>? executeValue, Predicate<object?>? canExecuteValue = null) { execute = executeValue ?? throw new ArgumentNullException(nameof(executeValue)); canExecute = canExecuteValue; } public bool CanExecute(object? parameter) { return canExecute == null ? true : canExecute(parameter); } public void Execute(object? parameter) { execute?.Invoke(parameter); } } public class ImgUrlConverter : IValueConverter { private static readonly object objLock = new object(); private Dictionary<string, ImageSource> imgCache = new Dictionary<string, ImageSource>(); public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string imgUrl = value?.ToString(); if (string.IsNullOrWhiteSpace(imgUrl) || !File.Exists(imgUrl)) { return null; } lock (objLock) { if (imgCache.TryGetValue(imgUrl, out ImageSource imageSource)) { return imageSource; } using (FileStream fs = new FileStream(imgUrl, FileMode.Open, FileAccess.Read, FileShare.Read)) { BitmapImage bmi = new BitmapImage(); bmi.BeginInit(); bmi.StreamSource = fs; bmi.CacheOption = BitmapCacheOption.OnLoad; bmi.EndInit(); if (bmi.CanFreeze) { bmi.Freeze(); } imgCache.TryAdd(imgUrl, bmi); return bmi; } } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } public class Book { public int Id { get; set; } public string ImgUrl { get; set; } public override string ToString() { return $"Id:{Id},ImgUrl:{ImgUrl}"; } } }



The thread '.NET TP Worker' (19800) has exited with code 0 (0x0). The thread '.NET TP Worker' (22288) has exited with code 0 (0x0). The thread '.NET TP Worker' (16372) has exited with code 0 (0x0). Id:1,ImgUrl:../../../Images\1108.jpg Id:134513275,ImgUrl:../../../Images\398.jpg Id:300000001,ImgUrl:../../../Images\1191.jpg The thread '.NET TP Worker' (3300) has exited with code 0 (0x0). The thread '.NET TP Worker' (3900) has exited with code 0 (0x0). Id:299999959,ImgUrl:../../../Images\986.jpg Id:299999944,ImgUrl:../../../Images\415.jpg Id:299999929,ImgUrl:../../../Images\395.jpg Id:299999917,ImgUrl:../../../Images\382.jpg Id:299999893,ImgUrl:../../../Images\356.jpg Id:299999872,ImgUrl:../../../Images\332.jpg

此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。