簡素な仮想化パネル
こんなXAML
<Window x:Class="Test.FastCanvas.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Test.FastCanvas" Title="MainWindow" Height="350" Width="525"> <ScrollViewer x:Name="scroller" HorizontalScrollBarVisibility="Visible"> <local:FastCanvas x:Name="canvas" /> </ScrollViewer> </Window>
こんなコード(usingは省略してます)
namespace Test.FastCanvas { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); for (int y = 0; y < 1000; y++) { for (int x = 0; x < 100; x++) { var element = new Ellipse() { Width = 32, Height = 32, Fill = Brushes.LightPink }; element.MouseEnter += (s,e) =>{ ((Ellipse)s).Fill = Brushes.Blue; }; element.MouseLeave += (s, e) => { ((Ellipse)s).Fill = Brushes.LightPink; }; Canvas.SetLeft(element, x * 32); Canvas.SetTop(element, y * 32); canvas.AddElement(element); } } canvas.Height = 32 * 1000; canvas.Width = 32 * 100; scroller.ScrollChanged += scroller_ScrollChanged; } void scroller_ScrollChanged(object sender, ScrollChangedEventArgs e) { var rect = new Rect( scroller.HorizontalOffset, scroller.VerticalOffset, scroller.ViewportWidth, scroller.ViewportHeight); canvas.SetViewport(rect); } } }
こんなコントロール(usingは省略してます)
namespace Test.FastCanvas { public class FastCanvas : Canvas { private HashSet<UIElement> _virtualChildren = new HashSet<UIElement>(); public void AddElement(UIElement element) { _virtualChildren.Add(element); } public void RemoveElement(UIElement element) { _virtualChildren.Remove(element); } public void SetViewport(Rect rect) { foreach(FrameworkElement child in _virtualChildren) { var childRect = new Rect(Canvas.GetLeft(child), Canvas.GetTop(child), child.Width, child.Height); if (!rect.IntersectsWith(childRect)) { if (Children.Contains(child)) Children.Remove(child); } else { if (!Children.Contains(child)) Children.Add(child); } } } } }
分かり易く仮想化を説明する教材として作ったものです。
ScrollChangedを使うと古風なので(あとレイアウトパスが過剰に走って遅いので)、データバインディングでViewportを設定できるようにするとMVVM風ですよね。マルチバリューコンバーターの出番です。
起動時間と、マウスヒットテストは仮想化の有無だけで相当違います。それを体感するためのサンプルと思っていただけたら。
ちなみに、同じコードでFastCanvasをCanvasに入れ替え、ScrollChangedのイベントをやめるだけで、仮想化自体の威力は体感できると思います。