ソースコードだけ確認したい場合はこちら - Github gist
用途:表示範囲内に限定して更新処理したい
- 例え1000件程度のデータだろうと表示範囲外コンテンツの更新処理が行われないようにしたい場合。
方法:ListViewBase.ContainerContentChanging イベントの活用
InRecycleQueue で表示範囲の内外を判定
ContainerContentChanging
イベントの InRecycleQueue
が true
の場合、表示範囲外にアイテムが出たと判定できます。
private void CutListItemsListView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
var item = args.Item as StoryboardCutViewModel;
if (args.InRecycleQueue)
{
_vm.RealizedVMItems.Remove(item);
Debug.WriteLine($"{item} InRecycleQueue");
}
else
{
_vm.RealizedVMItems.Add(item);
Debug.WriteLine($"{item} Displaying!");
}
}
カスタム添付プロパティとして実装
ListViewExtensions_RealizedCollection.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#nullable enable
namespace SampleApp.Views.Extensions;
public static partial class ListViewExtensions
{
public static ICollection<object>? GetRealizedCollection(DependencyObject obj)
{
return (ICollection<object>?)obj.GetValue(RealizedCollectionProperty);
}
public static void SetRealizedCollection(DependencyObject obj, ICollection<object> value)
{
obj.SetValue(RealizedCollectionProperty, value);
}
public static readonly DependencyProperty RealizedCollectionProperty =
DependencyProperty.RegisterAttached("RealizedCollection", typeof(ICollection<object>), typeof(ListViewExtensions), new PropertyMetadata(null, OnRealizedCollectionChanged));
private static void OnRealizedCollectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var list = (ListViewBase)d;
if (e.OldValue is ICollection<object> oldCollection)
{
list.ContainerContentChanging -= List_ContainerContentChanging;
}
if (e.NewValue is ICollection<object> collection)
{
list.ContainerContentChanging += List_ContainerContentChanging;
}
static void List_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
var collection = GetRealizedCollection(sender);
if (collection == null)
{
sender.ContainerContentChanging -= List_ContainerContentChanging;
return;
}
if (args.InRecycleQueue)
{
collection.Remove(args.Item);
}
else
{
collection.Add(args.Item);
}
}
}
}
DataContextに任意のViewModelインスタンスがセットされてる状態で、以下のように使用します。
<ListView
ItemsSource="{Binding DisplayItems}"
xmlns:myViewExt="using:SampleApp.Views.Extensions"
myViewExt:ListViewExtensions.RealizedCollection="{Binding RealizedVMItems}"
/>
おまけ:表示範囲にIn/Outしたタイミングでコマンドを実行するカスタム添付プロパティ
ListViewExtensions_RealizedCommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#nullable enable
namespace SampleApp.Views.Extensions;
public static partial class ListViewExtensions
{
public static ICommand? GetRealizedCommand(DependencyObject obj)
{
return (ICommand?)obj.GetValue(RealizedCommandProperty);
}
public static void SetRealizedCommand(DependencyObject obj, ICommand? value)
{
obj.SetValue(RealizedCommandProperty, value);
}
public static readonly DependencyProperty RealizedCommandProperty =
DependencyProperty.RegisterAttached("RealizedCommand", typeof(ICommand), typeof(ListViewExtensions), new PropertyMetadata(null, OnRealizeCommandPropertyChanged));
public static ICommand? GetDerealizedCommand(DependencyObject obj)
{
return (ICommand?)obj.GetValue(DerealizedCommandProperty);
}
public static void SetDerealizedCommand(DependencyObject obj, ICommand? value)
{
obj.SetValue(DerealizedCommandProperty, value);
}
public static readonly DependencyProperty DerealizedCommandProperty =
DependencyProperty.RegisterAttached("DerealizedCommand", typeof(ICommand), typeof(ListViewExtensions), new PropertyMetadata(null, OnRealizeCommandPropertyChanged));
private static void OnRealizeCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var list = (ListViewBase)d;
if (e.OldValue is ICommand oldCommand)
{
list.ContainerContentChanging -= ForCommand_ContainerContentChanging;
}
if (e.NewValue is ICommand newCommand)
{
list.ContainerContentChanging -= ForCommand_ContainerContentChanging;
list.ContainerContentChanging += ForCommand_ContainerContentChanging;
}
}
private static void ForCommand_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if ((args.InRecycleQueue ? GetDerealizedCommand(sender) : GetRealizedCommand(sender)) is { } command
&& command.CanExecute(args.Item)
)
{
command.Execute(args.Item);
}
}
}