728x90
728x170
▶ Customer.cs
namespace TestProject
{
/// <summary>
/// 고객
/// </summary>
public class Customer
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 바이트 배열
/// </summary>
/// <remarks>테스트를 위한 더미 데이터</remarks>
private byte[] byteArray = new byte[100];
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region ID - ID
/// <summary>
/// ID
/// </summary>
public int ID { get; set; }
#endregion
#region 성명 - Name
/// <summary>
/// 성명
/// </summary>
public string Name { get; set; }
#endregion
}
}
728x90
▶ IItemProvider.cs
using System.Collections.Generic;
namespace TestProject
{
/// <summary>
/// 항목 공급자 인터페이스
/// </summary>
/// <typeparam name="TItem">항목 타입</typeparam>
public interface IItemProvider<TItem>
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
#region 카운트 가져오기 - FetchCount()
/// <summary>
/// 카운트 가져오기
/// </summary>
/// <returns>카운트</returns>
int FetchCount();
#endregion
#region 범위 가져오기 - FetchRange(startIndex, count)
/// <summary>
/// 범위 가져오기
/// </summary>
/// <param name="startIndex">시작 인덱스</param>
/// <param name="count">카운트</param>
/// <returns>항목 리스트</returns>
IList<TItem> FetchRange(int startIndex, int count);
#endregion
}
}
300x250
▶ CustomerProvider.cs
using System.Collections.Generic;
using System.Threading;
namespace TestProject
{
/// <summary>
/// 고객 공급자
/// </summary>
public class CustomerProvider : IItemProvider<Customer>
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 카운트
/// </summary>
private readonly int count;
/// <summary>
/// 페치 지연 시간
/// </summary>
private readonly int fetchDelay;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - CustomerProvider(count, fetchDelay)
/// <summary>
/// 생성자
/// </summary>
/// <param name="count">카운트</param>
/// <param name="fetchDelay">페치 지연 시간</param>
public CustomerProvider(int count, int fetchDelay)
{
this.count = count;
this.fetchDelay = fetchDelay;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 카운트 가져오기 - FetchCount()
/// <summary>
/// 카운트 가져오기
/// </summary>
/// <returns>카운트</returns>
public int FetchCount()
{
Thread.Sleep(this.fetchDelay);
return this.count;
}
#endregion
#region 범위 가져오기 - FetchRange(startIndex, count)
/// <summary>
/// 범위 가져오기
/// </summary>
/// <param name="startIndex">시작 인덱스</param>
/// <param name="count">카운트</param>
/// <returns>고객 리스트</returns>
public IList<Customer> FetchRange(int startIndex, int count)
{
Thread.Sleep(this.fetchDelay);
List<Customer> list = new List<Customer>();
for(int i = startIndex; i < startIndex + count; i++)
{
Customer customer = new Customer { ID = i + 1, Name = "고객 " + (i + 1) };
list.Add(customer);
}
return list;
}
#endregion
}
}
▶ VirtualizingCollection.cs
using System;
using System.Collections;
using System.Collections.Generic;
namespace TestProject
{
/// <summary>
/// 가상화 컬렉션
/// </summary>
/// <typeparam name="TItem">항목 타입</typeparam>
public class VirtualizingCollection<TItem> : IList<TItem>, IList
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 항목 공급자
/// </summary>
private readonly IItemProvider<TItem> itemProvider;
/// <summary>
/// 페이지 크기
/// </summary>
private readonly int pageSize = 100;
/// <summary>
/// 페이지 타임아웃
/// </summary>
private readonly long pageTimeout = 10000;
/// <summary>
/// 카운트
/// </summary>
private int count = -1;
/// <summary>
/// 페이지 딕셔너리
/// </summary>
private readonly Dictionary<int, IList<TItem>> pageDictionary = new Dictionary<int, IList<TItem>>();
/// <summary>
/// 페이지 터치 시간 딕셔너리
/// </summary>
private readonly Dictionary<int, DateTime> pageTouchTimeDictionary = new Dictionary<int, DateTime>();
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 항목 공급자 - ItemProvider
/// <summary>
/// 항목 공급자
/// </summary>
public IItemProvider<TItem> ItemProvider
{
get
{
return this.itemProvider;
}
}
#endregion
#region 페이지 크기 - PageSize
/// <summary>
/// 페이지 크기
/// </summary>
public int PageSize
{
get
{
return this.pageSize;
}
}
#endregion
#region 페이지 타임아웃 - PageTimeout
/// <summary>
/// 페이지 타임아웃
/// </summary>
public long PageTimeout
{
get
{
return this.pageTimeout;
}
}
#endregion
#region 카운트 - Count
/// <summary>
/// 카운트
/// </summary>
public virtual int Count
{
get
{
if(this.count == -1)
{
LoadCount();
}
return this.count;
}
protected set
{
this.count = value;
}
}
#endregion
#region 인덱서 - this[index]
/// <summary>
/// 인덱서
/// </summary>
/// <param name="index">인덱스</param>
/// <returns>항목</returns>
public TItem this[int index]
{
get
{
int pageIndex = index / PageSize;
int pageOffset = index % PageSize;
RequestPage(pageIndex);
if(pageOffset > PageSize / 2 && pageIndex < Count / PageSize)
{
RequestPage(pageIndex + 1);
}
if(pageOffset < PageSize / 2 && pageIndex > 0)
{
RequestPage(pageIndex - 1);
}
CleanUpPages();
if(this.pageDictionary[pageIndex] == null)
{
return default(TItem);
}
return this.pageDictionary[pageIndex][pageOffset];
}
set
{
throw new NotSupportedException();
}
}
#endregion
#region 동기 루트 - SyncRoot
/// <summary>
/// 동기 루트
/// </summary>
public object SyncRoot
{
get
{
return this;
}
}
#endregion
#region 동기화 여부 - IsSynchronized
/// <summary>
/// 동기화 여부
/// </summary>
public bool IsSynchronized
{
get
{
return false;
}
}
#endregion
#region 읽기 전용 여부 - IsReadOnly
/// <summary>
/// 읽기 전용 여부
/// </summary>
public bool IsReadOnly
{
get
{
return true;
}
}
#endregion
#region 고정 크기 여부 - IsFixedSize
/// <summary>
/// 고정 크기 여부
/// </summary>
public bool IsFixedSize
{
get
{
return false;
}
}
#endregion
#region (IList) 인덱서 - this[index]
/// <summary>
/// 인덱서
/// </summary>
/// <param name="index">인덱스</param>
/// <returns>항목</returns>
object IList.this[int index]
{
get
{
return this[index];
}
set
{
throw new NotSupportedException();
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - VirtualizingCollection(itemProvider)
/// <summary>
/// 생성자
/// </summary>
/// <param name="itemProvider">항목 공급자</param>
public VirtualizingCollection(IItemProvider<TItem> itemProvider)
{
this.itemProvider = itemProvider;
}
#endregion
#region 생성자 - VirtualizingCollection(itemProvider, pageSize)
/// <summary>
/// 생성자
/// </summary>
/// <param name="itemProvider">항목 공급자</param>
/// <param name="pageSize">페이지 크기</param>
public VirtualizingCollection(IItemProvider<TItem> itemProvider, int pageSize)
{
this.itemProvider = itemProvider;
this.pageSize = pageSize;
}
#endregion
#region 생성자 - VirtualizingCollection(itemProvider, pageSize, pageTimeout)
/// <summary>
/// 생성자
/// </summary>
/// <param name="itemProvider">항목 공급자</param>
/// <param name="pageSize">페이지 크기</param>
/// <param name="pageTimeout">페이지 타임아웃</param>
public VirtualizingCollection(IItemProvider<TItem> itemProvider, int pageSize, int pageTimeout)
{
this.itemProvider = itemProvider;
this.pageSize = pageSize;
this.pageTimeout = pageTimeout;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 페이지들 지우기 - CleanUpPages()
/// <summary>
/// 페이지들 지우기
/// </summary>
public void CleanUpPages()
{
List<int> keyList = new List<int>(this.pageTouchTimeDictionary.Keys);
foreach(int key in keyList)
{
if(key != 0 && (DateTime.Now - this.pageTouchTimeDictionary[key]).TotalMilliseconds > PageTimeout)
{
this.pageDictionary.Remove(key);
this.pageTouchTimeDictionary.Remove(key);
}
}
}
#endregion
#region 열거자 구하기 - GetEnumerator()
/// <summary>
/// 열거자 구하기
/// </summary>
/// <returns>열거자</returns>
public IEnumerator<TItem> GetEnumerator()
{
for(int i = 0; i < Count; i++)
{
yield return this[i];
}
}
#endregion
#region 포함 여부 구하기 - Contains(item)
/// <summary>
/// 포함 여부 구하기
/// </summary>
/// <param name="item">항목</param>
/// <returns>포함 여부</returns>
public bool Contains(TItem item)
{
return false;
}
#endregion
#region 추가하기 - Add(item)
/// <summary>
/// 추가하기
/// </summary>
/// <param name="item">항목</param>
public void Add(TItem item)
{
throw new NotSupportedException();
}
#endregion
#region 삽입하기 - Insert(index, item)
/// <summary>
/// 삽입하기
/// </summary>
/// <param name="index">인덱스</param>
/// <param name="item">항목</param>
public void Insert(int index, TItem item)
{
throw new NotSupportedException();
}
#endregion
#region 인덱스 구하기 - IndexOf(item)
/// <summary>
/// 인덱스 구하기
/// </summary>
/// <param name="item">항목</param>
/// <returns>인덱스</returns>
public int IndexOf(TItem item)
{
return -1;
}
#endregion
#region 제거하기 - RemoveAt(index)
/// <summary>
/// 제거하기
/// </summary>
/// <param name="index">인덱스</param>
public void RemoveAt(int index)
{
throw new NotSupportedException();
}
#endregion
#region 제거하기 - Remove(item)
/// <summary>
/// 제거하기
/// </summary>
/// <param name="item">항목</param>
/// <returns>처리 결과</returns>
public bool Remove(TItem item)
{
throw new NotSupportedException();
}
#endregion
#region 지우기 - Clear()
/// <summary>
/// 지우기
/// </summary>
public void Clear()
{
throw new NotSupportedException();
}
#endregion
#region 복사하기 - CopyTo(itemArray, arrayIndex)
/// <summary>
/// 복사하기
/// </summary>
/// <param name="itemArray">항목 배열</param>
/// <param name="arrayIndex">배열 인덱스</param>
public void CopyTo(TItem[] itemArray, int arrayIndex)
{
throw new NotSupportedException();
}
#endregion
#region (IEnumerable) 열거자 구하기 - GetEnumerator()
/// <summary>
/// 열거자 구하기
/// </summary>
/// <returns>열거자</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region (IList) 포함 여부 구하기 - Contains(value)
/// <summary>
/// 포함 여부 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>포함 여부</returns>
bool IList.Contains(object value)
{
return Contains((TItem)value);
}
#endregion
#region (IList) 추가하기 - Add(value)
/// <summary>
/// 추가하기
/// </summary>
/// <param name="value">값</param>
/// <returns>인덱스</returns>
int IList.Add(object value)
{
throw new NotSupportedException();
}
#endregion
#region (IList) 삽입하기 - Insert(index, value)
/// <summary>
/// 삽입하기
/// </summary>
/// <param name="index">인덱스</param>
/// <param name="value">값</param>
void IList.Insert(int index, object value)
{
Insert(index, (TItem)value);
}
#endregion
#region (IList) 인덱스 구하기 - IndexOf(value)
/// <summary>
/// 인덱스 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>인덱스</returns>
int IList.IndexOf(object value)
{
return IndexOf((TItem) value);
}
#endregion
#region (IList) 제거하기 - Remove(value)
/// <summary>
/// 제거하기
/// </summary>
/// <param name="value">값</param>
void IList.Remove(object value)
{
throw new NotSupportedException();
}
#endregion
#region (ICollection) 복사하기 - CopyTo(array, index)
/// <summary>
/// 복사하기
/// </summary>
/// <param name="array">배열</param>
/// <param name="index">인덱스</param>
void ICollection.CopyTo(Array array, int index)
{
throw new NotSupportedException();
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 카운트 가져오기 - FetchCount()
/// <summary>
/// 카운트 가져오기
/// </summary>
/// <returns>카운트</returns>
protected int FetchCount()
{
return ItemProvider.FetchCount();
}
#endregion
#region 카운트 로드하기 - LoadCount()
/// <summary>
/// 카운트 로드하기
/// </summary>
protected virtual void LoadCount()
{
Count = FetchCount();
}
#endregion
#region 페이지 채우기 - PopulatePage(pageIndex, itemList)
/// <summary>
/// 페이지 채우기
/// </summary>
/// <param name="pageIndex">페이지 인덱스</param>
/// <param name="itemList">항목 리스트</param>
protected virtual void PopulatePage(int pageIndex, IList<TItem> itemList)
{
if(this.pageDictionary.ContainsKey(pageIndex))
{
this.pageDictionary[pageIndex] = itemList;
}
}
#endregion
#region 페이지 가져오기 - FetchPage(pageIndex)
/// <summary>
/// 페이지 가져오기
/// </summary>
/// <param name="pageIndex">페이지 인덱스</param>
/// <returns>항목 리스트</returns>
protected IList<TItem> FetchPage(int pageIndex)
{
return ItemProvider.FetchRange(pageIndex * PageSize, PageSize);
}
#endregion
#region 페이지 로드하기 - LoadPage(pageIndex)
/// <summary>
/// 페이지 로드하기
/// </summary>
/// <param name="pageIndex">페이지 인덱스</param>
protected virtual void LoadPage(int pageIndex)
{
PopulatePage(pageIndex, FetchPage(pageIndex));
}
#endregion
#region 페이지 요청하기 - RequestPage(pageIndex)
/// <summary>
/// 페이지 요청하기
/// </summary>
/// <param name="pageIndex">페이지 인덱스</param>
protected virtual void RequestPage(int pageIndex)
{
if(!this.pageDictionary.ContainsKey(pageIndex))
{
this.pageDictionary.Add(pageIndex, null);
this.pageTouchTimeDictionary.Add(pageIndex, DateTime.Now);
LoadPage(pageIndex);
}
else
{
this.pageTouchTimeDictionary[pageIndex] = DateTime.Now;
}
}
#endregion
}
}
▶ AsyncVirtualizingCollection.cs
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;
namespace TestProject
{
/// <summary>
/// 비동기 가상화 컬렉션
/// </summary>
/// <typeparam name="TItem">항목 타입</typeparam>
public class AsyncVirtualizingCollection<TItem> : VirtualizingCollection<TItem>, INotifyCollectionChanged, INotifyPropertyChanged
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Event
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 속성 변경시 이벤트 - PropertyChanged
/// <summary>
/// 속성 변경시 이벤트
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region 컬렉션 변경시 이벤트 - CollectionChanged
/// <summary>
/// 컬렉션 변경시 이벤트
/// </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 동기화 컨텍스트
/// </summary>
private readonly SynchronizationContext synchronizationContext;
/// <summary>
/// 로딩 여부
/// </summary>
private bool isLoading;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 동기화 컨텍스트 - SynchronizationContext
/// <summary>
/// 동기화 컨텍스트
/// </summary>
protected SynchronizationContext SynchronizationContext
{
get
{
return this.synchronizationContext;
}
}
#endregion
#region 로딩 여부 - IsLoading
/// <summary>
/// 로딩 여부
/// </summary>
public bool IsLoading
{
get
{
return this.isLoading;
}
set
{
if(value != this.isLoading)
{
this.isLoading = value;
}
FirePropertyChangedEvent("IsLoading");
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - AsyncVirtualizingCollection(itemProvider)
/// <summary>
/// 생성자
/// </summary>
/// <param name="itemProvider">항목 공급자</param>
public AsyncVirtualizingCollection(IItemProvider<TItem> itemProvider) : base(itemProvider)
{
this.synchronizationContext = SynchronizationContext.Current;
}
#endregion
#region 생성자 - AsyncVirtualizingCollection(itemProvider, pageSize)
/// <summary>
/// 생성자
/// </summary>
/// <param name="itemProvider">항목 공급자</param>
/// <param name="pageSize">페이지 크기</param>
public AsyncVirtualizingCollection(IItemProvider<TItem> itemProvider, int pageSize) : base(itemProvider, pageSize)
{
this.synchronizationContext = SynchronizationContext.Current;
}
#endregion
#region 생성자 - AsyncVirtualizingCollection(itemProvider, pageSize, pageTimeout)
/// <summary>
/// 생성자
/// </summary>
/// <param name="itemProvider">항목 공급자</param>
/// <param name="pageSize">페이지 크기</param>
/// <param name="pageTimeout">페이지 타임아웃</param>
public AsyncVirtualizingCollection(IItemProvider<TItem> itemProvider, int pageSize, int pageTimeout) : base(itemProvider, pageSize, pageTimeout)
{
this.synchronizationContext = SynchronizationContext.Current;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 속성 변경시 이벤트 발생시키기 - FirePropertyChangedEvent(e)
/// <summary>
/// 속성 변경시 이벤트 발생시키기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected virtual void FirePropertyChangedEvent(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
#endregion
#region 컬렉션 변경시 이벤트 발생시키기 - FireCollectionChangedEvent(e)
/// <summary>
/// 컬렉션 변경시 이벤트 발생시키기
/// </summary>
/// <param name="e">이벤트 인자</param>
protected virtual void FireCollectionChangedEvent(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
}
#endregion
#region 카운트 로드하기 - LoadCount()
/// <summary>
/// 카운트 로드하기
/// </summary>
protected override void LoadCount()
{
Count = 0;
IsLoading = true;
ThreadPool.QueueUserWorkItem(LoadCount);
}
#endregion
#region 페이지 로드하기 - LoadPage(index)
/// <summary>
/// 페이지 로드하기
/// </summary>
/// <param name="index">인덱스</param>
protected override void LoadPage(int index)
{
IsLoading = true;
ThreadPool.QueueUserWorkItem(LoadPage, index);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 속성 변경시 이벤트 발생시키기 - FirePropertyChangedEvent(propertyName)
/// <summary>
/// 속성 변경시 이벤트 발생시키기
/// </summary>
/// <param name="propertyName">속성명</param>
private void FirePropertyChangedEvent(string propertyName)
{
FirePropertyChangedEvent(new PropertyChangedEventArgs(propertyName));
}
#endregion
#region 컬렉션 변경시 리셋 이벤트 발생시키기 - FireCollectionChangedResetEvent()
/// <summary>
/// 컬렉션 변경시 리셋 이벤트 발생시키기
/// </summary>
private void FireCollectionChangedResetEvent()
{
FireCollectionChangedEvent(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
#endregion
#region 카운트 로드하기 - LoadCount(argument)
/// <summary>
/// 카운트 로드하기
/// </summary>
/// <param name="argument">인자</param>
private void LoadCount(object argument)
{
int count = FetchCount();
SynchronizationContext.Send(LoadCountCompleted, count);
}
#endregion
#region 카운트 로드 완료시 처리하기 - LoadCountCompleted(argument)
/// <summary>
/// 카운트 로드 완료시 처리하기
/// </summary>
/// <param name="argument">인자</param>
private void LoadCountCompleted(object argument)
{
Count = (int)argument;
IsLoading = false;
FireCollectionChangedResetEvent();
}
#endregion
#region 페이지 로드하기 - LoadPage(argument)
/// <summary>
/// 페이지 로드하기
/// </summary>
/// <param name="argument">인자</param>
private void LoadPage(object argument)
{
int pageIndex = (int)argument;
IList<TItem> itemList = FetchPage(pageIndex);
SynchronizationContext.Send(LoadPageCompleted, new object[]{ pageIndex, itemList });
}
#endregion
#region 페이지 로드 완료시 처리하기 - LoadPageCompleted(argument)
/// <summary>
/// 페이지 로드 완료시 처리하기
/// </summary>
/// <param name="argument">인자</param>
private void LoadPageCompleted(object argument)
{
int pageIndex = (int)((object[]) argument)[0];
IList<TItem> itemList = (IList<TItem>)((object[])argument)[1];
PopulatePage(pageIndex, itemList);
IsLoading = false;
FireCollectionChangedResetEvent();
}
#endregion
}
}
▶ MainWindow.xaml
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800"
Height="600"
Title="데이터 가상화 사용하기"
FontFamily="나눔고딕코딩"
FontSize="16">
<Window.Resources>
<Style x:Key="ListViewStyleKey" TargetType="{x:Type ListView}">
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True" />
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="True" />
<Setter Property="ListView.ItemsSource" Value="{Binding}" />
<Setter Property="ListView.View">
<Setter.Value>
<GridView>
<GridViewColumn Header="ID"
Width="100">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="성명"
Width="150">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoading}" Value="True">
<Setter Property="ListView.Cursor" Value="Wait" />
<Setter Property="ListView.Background" Value="LightGray" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GroupBox Header="항목 공급자" Grid.Row="0">
<StackPanel
Orientation="Horizontal"
Margin="0 10 0 0">
<TextBlock
VerticalAlignment="Center"
Margin="10 0 0 0"
TextAlignment="Right"
Text="항목 수 : " />
<TextBox Name="itemCountTextBox"
VerticalAlignment="Center"
Width="70"
Height="25"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
Text="1000000" />
<TextBlock
VerticalAlignment="Center"
Margin="10 0 0 0"
TextAlignment="Right"
Text="조회 시간 (밀리초) : " />
<TextBox Name="fetchDelayTextBox"
VerticalAlignment="Center"
Width="50"
Height="25"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
Text="1000" />
</StackPanel>
</GroupBox>
<GroupBox Header="컬렉션" Grid.Row="1"
Margin="0 10 0 0">
<StackPanel Margin="10 10 0 0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="타입 :"
VerticalAlignment="Center"
TextAlignment="Right" />
<RadioButton Name="normalRadioButton"
VerticalAlignment="Center"
Margin="10 0 0 0"
GroupName="rbGroup"
Content="List<T>" />
<RadioButton Name="virtualizingRadioButton"
VerticalAlignment="Center"
Margin="10 0 0 0"
GroupName="rbGroup"
Content="VirtualizingList<T>" />
<RadioButton Name="asyncRadioButton"
VerticalAlignment="Center"
Margin="10 0 0 0"
GroupName="rbGroup"
IsChecked="True"
Content="AsyncVirtualizingList<T>" />
</StackPanel>
<StackPanel
Margin="0 10 0 0"
Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
TextAlignment="Right"
Text="페이지 크기 : " />
<TextBox Name="pageSizeTextBox"
VerticalAlignment="Center"
Width="40"
Height="25"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
Text="100" />
<TextBlock
VerticalAlignment="Center"
Margin="10 0 0 0"
TextAlignment="Right"
Text="페이지 타임아웃 (초) : " />
<TextBox Name="pageTimeoutTextBox"
VerticalAlignment="Center"
Width="30"
Height="25"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
Text="30" />
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Grid.Row="2"
Margin="0 10 0 0"
Orientation="Horizontal">
<TextBlock
Margin="10 0 0 0"
VerticalAlignment="Center"
Text="메모리 사용량 :" />
<TextBlock Name="memoryUsageTextBox"
VerticalAlignment="Center"
Margin="10 0 0 0"
MinWidth="40" />
<Button
VerticalAlignment="Center"
Margin="10 0 0 0"
Width="100"
Height="30"
Content="갱신"
Click="refreshButton_Click" />
<Rectangle Name="rectangle"
VerticalAlignment="Center"
Margin="10 0 0 0"
Width="20"
Height="20"
Fill="Blue">
<Rectangle.RenderTransform>
<RotateTransform
CenterX="10"
CenterY="10"
Angle="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="rectangle"
Storyboard.TargetProperty="(TextBlock.RenderTransform).(RotateTransform.Angle)"
From="0"
To="360"
Duration="0:0:5"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
<TextBlock
Margin="10 0 0 0"
VerticalAlignment="Center"
FontStyle="Italic"
FontSize="12"
Text="애니메이션 일시 중지는 UI 스레드가 중단되었음을 나타냅니다." />
</StackPanel>
<ListView Grid.Row="3"
Style="{DynamicResource ListViewStyleKey}"
Margin="0 10 0 0" />
</Grid>
</Window>
▶ MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Threading;
namespace TestProject
{
/// <summary>
/// 메인 윈도우
/// </summary>
public partial class MainWindow : Window
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainWindow()
/// <summary>
/// 생성자
/// </summary>
public MainWindow()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 1);
timer.Tick += timer_Tick;
timer.Start();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 타이머 틱 처리하기 - timer_Tick(sender, e)
/// <summary>
/// 타이머 틱 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void timer_Tick(object sender, EventArgs e)
{
this.memoryUsageTextBox.Text = string.Format("{0:0.00} MB", GC.GetTotalMemory(true) / 1024.0 / 1024.0);
}
#endregion
#region 갱신 버튼 클릭시 처리하기 - refreshButton_Click(sender, e)
/// <summary>
/// 갱신 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void refreshButton_Click(object sender, RoutedEventArgs e)
{
int itemCount = int.Parse(this.itemCountTextBox.Text );
int fetchDelay = int.Parse(this.fetchDelayTextBox.Text);
CustomerProvider customerProvider = new CustomerProvider(itemCount, fetchDelay);
int pageSize = int.Parse(this.pageSizeTextBox.Text );
int pageTimeout = int.Parse(this.pageTimeoutTextBox.Text);
if(this.normalRadioButton.IsChecked.Value)
{
DataContext = new List<Customer>(customerProvider.FetchRange(0, customerProvider.FetchCount()));
}
else if(this.virtualizingRadioButton.IsChecked.Value)
{
DataContext = new VirtualizingCollection<Customer>(customerProvider, pageSize);
}
else if(this.asyncRadioButton.IsChecked.Value)
{
DataContext = new AsyncVirtualizingCollection<Customer>(customerProvider, pageSize, pageTimeout * 1000);
}
}
#endregion
}
}
※ .NET 버전 3.5에서만 작동함에 주의한다.
728x90
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] Vector 구조체 : 벡터 각도 구하기 (0) | 2021.08.19 |
---|---|
[C#/WPF] ResourceDictionary 클래스 : MergedDictionaries 속성을 사용해 Application 객체의 리소스 사용하기 (0) | 2021.07.08 |
[C#/WPF] ControlTemplate 엘리먼트 : Thumb/ScrollBar/ListBoxItem 엘리먼트 정의하기 (0) | 2021.06.07 |
[C#/WPF] HwndSource 클래스 : FromHwnd 정적 메소드를 사용해 윈도우 핸들로 윈도우 구하기 (0) | 2021.06.07 |
[C#/WPF] 잠금 화면 설정하기 (기능 개선) (0) | 2021.05.31 |
[C#/WPF] PathGeometry 클래스 : GraphicsPath 객체에서 패스 지오메트리 구하기 (0) | 2021.05.13 |
[C#/WPF] 누겟 설치 : AvalonEdit (0) | 2021.05.10 |
[C#/WPF] 코드 편집기(Code Editor) 사용하기 (0) | 2021.05.07 |
[C#/WPF] Dispatcher 클래스 : DisableProcessing 메소드를 사용해 디스패처 비활성하기 (0) | 2021.05.01 |
[C#/WPF] Dispatcher 클래스 : PushFrame 메소드를 사용해 UI 업데이트 하기 (0) | 2021.04.30 |