成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳談WPF開發中的數據虛擬化

開發 后端
從UI的觀點上看,WPF為有效的處理大型集合提供了一些聰明的UI虛擬化特征。但那沒有為數據歸檔虛擬化提供通用的方法。當有人在互聯網論壇上發貼討論數據虛擬化時,沒有一個人提供一個解決方案(至少我沒有看到)。本文提出這個的一個解決方案。

編輯推薦專題:讓你的代碼“炫”起來——WPF開發教程

UI虛擬化

當一個WPF的ItemControl被綁定到一個大型集合的數據源時,如果可以UI虛擬化,該控件將只為那些在可以看到的項創見可視化的容器(加上面和下面的少許)。這是一個完整集合中有代表性的一小部分。用戶移動滾動條時,將為那些滾動到可視區域的項創建新的可視化容器,那些不再可見的項的容器將被銷毀。當容器設置為循環使用時,它將再使用可視化容器代替不斷的創建和銷毀可視化容器,避免對象的實例化和垃圾回收器的過度工作。

數據虛擬化

數據虛擬化是指綁定到ItemControl的真實的數據對象的歸檔虛擬化的時間段。數據虛擬化不是由WPF提供的。作為對比,基本數據對象的小集合對內存的消耗不是很多;但是,大集合的內存消耗是非常嚴重的。另外,真實的檢索數據(例如,從數據庫)和實例化數據對象是很耗時的,尤其當是網絡數據調用時。因此,我們希望使用數據虛擬化機制來限制檢索的數據的數量和在內存中生成數據對象的數量。

解決方案

總覽

這個解決方案是只在ItemControl綁定到IList接口的實現時起作用,而不是IEumerable的實現,它并不枚舉整個列表,而只是讀取需要顯示的項。它使用Count屬性判斷集合的大小,推測并設置滾動的范圍。然后使用列表索引重新確定要在屏幕上顯示的項。因此,創建一個可以報告具有大量項的,并且可以只檢索需要的項的IList。

IItemsProvider 為了利用這個解決方案,下面的數據源必須能提供集合中項的數量,并且能夠提供完整集合的小塊(或頁)。這需要在IItemsProvider接口封裝。

/// 
/// Represents a provider of collection details.
///

/// The type of items in the collection.
public interface IItemsProvider
{
///
/// Fetches the total number of items available.
///

///
int FetchCount();

///
/// Fetches a range of items.
///

/// The start index.
/// The number of items to fetch.
///
IList FetchRange(int startIndex, int count);
}

如果下面的查詢是一個數據庫查詢,它是一個利用大多數據庫供應商都提供的COUNT()聚集函數和OFFSET與LIMIT表達式的一個IItemProvider接口的一個簡單實現。

VirtualizingCollection 這是一個執行數據虛擬化的IList的實現。VirtualizingCollection(T)把整個集合分裝到一定數量的頁中。根據需要把頁加載到內存中,在不需要時從釋放。

下面討論我們有興趣的部分。詳細信息請參考附件中的源代碼項目。

IList實現的***個方面是實現Count屬性。它通常被ItemsControl用來確定集合的大小,并呈現適當的滾動條。

private int _count = -1;
public virtual int Count
{
    get
    {
if (_count == -1)
        {
            LoadCount();
        }
        return _count;
    }
    protected set
    {
        _count = value;
    }
}
protected virtual void LoadCount()
{
    Count = FetchCount();
}

protected int FetchCount()
{
    return ItemsProvider.FetchCount();
}

Count屬性使用延遲和懶惰加載(lazy loading)模式。它使用特殊值-1作為未加載的標識。當***次讀取它時,它從ItemsProvider加載其實際的數量。

 IList接口的實現的另一個重要方面是索引的實現。

private int _count = -1;

public virtual int Count
{
get
{
if (_count == -1)
{
LoadCount();
}
return _count;
}
protected set
{
_count = value;
}
}

protected virtual void LoadCount()
{
Count = FetchCount();
}

protected int FetchCount()
{
return ItemsProvider.FetchCount();
}

這個索引是這個解決方案的一個聰明的操作。首先,它必須確定請求的項在哪個頁(pageIndex)中,在頁中的位置(pageOffset),然后調用RequestPage()方法請求該頁。

附加的步驟是然后根據pageOffset加載后一頁或前一頁。這基于一個假設,如果用戶正在瀏覽第0頁,那么他們有很高的機率接下來要滾動瀏覽第1頁。提前把數據取來,就可以無延遲的顯示。

然后調用CleanUpPages()清除(或卸載)所有不再使用的頁。

***,放置頁不可用的一個防御性的檢查, 當RequestPage沒有同步操作時是必要的,例如在子類AsyncVirtualizingCollection中。

// ...

private readonly Dictionary> _pages =
new Dictionary>();
private readonly Dictionary _pageTouchTimes =
new Dictionary();

protected virtual void RequestPage(int pageIndex)
{
if (!_pages.ContainsKey(pageIndex))
{
_pages.Add(pageIndex, null);
_pageTouchTimes.Add(pageIndex, DateTime.Now);
LoadPage(pageIndex);
}
else
{
_pageTouchTimes[pageIndex] = DateTime.Now;
}
}

protected virtual void PopulatePage(int pageIndex, IList page)
{
if (_pages.ContainsKey(pageIndex))
_pages[pageIndex] = page;
}

public void CleanUpPages()
{
List keys = new List(_pageTouchTimes.Keys);
foreach (int key in keys)
{
// page 0 is a special case, since the WPF ItemsControl
// accesses the first item frequently
if ( key != 0 && (DateTime.Now -
_pageTouchTimes[key]).TotalMilliseconds > PageTimeout )
{
_pages.Remove(key);
_pageTouchTimes.Remove(key);
}
}
}

頁存儲在以頁索引為鍵的字典(Dictionary)中。一個附加的字典(Dictionary)記錄著每個頁的***存取時間,它用于在CleanUpPages()方法中移除較長時間沒有存取的頁。

protected virtual void LoadPage(int pageIndex)
{
PopulatePage(pageIndex, FetchPage(pageIndex));
}

protected IList FetchPage(int pageIndex)
{
return

為完成該解決方案,FetchPage()執行從ItemProvider中抓取數據,LoadPage()方法完成調用PopulatePage方法獲取頁并把該頁存儲到字典(Dictionary)中的工作。

看起來好象有一些太多的不全邏輯的方法(a few too many inconsequential methods),但這樣設計是有原因的:每一個方法做且只做一件事,有助于提高代碼的可讀性,并使在子類中進行功能擴展和維護變得容易,下面可以看到。

類VirtualizingCollection實現了數據虛擬化的基本目標。不幸的是,在使用中,它有一個嚴重不足:數據抓取方法是全部同步執行的。這就是說它們要在UI線程中執行,造成一個緩慢的程序

AsyncVirtualizingCollection類AsyncVirtualizingCollection繼承自VirtualizingCollection,重載了Load方法,以實現數據的異步加載。

WPF中異步數據源的關鍵是在數據抓取完成后必須通知UI的數據綁定。在規則的對象中,是通過實現INotifyPropertyChanged接口實現的。對一個集合的實現,需要緊密的關系,INotifyCollectionChanged。那是ObservableCollection要使用的接口。

public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler h = CollectionChanged;
if (h != null)
h(this, e);
}

private void FireCollectionReset()
{
NotifyCollectionChangedEventArgs e =
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(e);
}

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler h = PropertyChanged;
if (h != null)
h(this, e);
}

private void FirePropertyChanged(string propertyName)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
OnPropertyChanged(e);
}

實現了INotifyCollectionChanged接口和INotifyPropertyChanged接口。提供數據綁定彈性最大化。這個實現沒有任何要注意的。

protected override void LoadCount()
{
Count = 0;
IsLoading = true;
ThreadPool.QueueUserWorkItem(LoadCountWork);
}

private void LoadCountWork(object args)
{
int count = FetchCount();
SynchronizationContext.Send(LoadCountCompleted, count);
}

private void LoadCountCompleted(object args)
{
Count = (int)args;
IsLoading = false;
FireCollectionReset();
}

在重載的LoadCount()方法中,抓取是由ThreadPool(線程池)異步調用的。一旦完成,就會重置Count,UI的更新是由INotifyCollectionChanged接口調用FireCollectionReset方法實現的。注意LoadCountCompleted方法會在UI線程通過SynchronizationContext再一次被調用。假定集合的實例在UI線程中被創建,SynchronationContext屬性就會被設置。

protected override void LoadPage(int index){IsLoading = true;
ThreadPool.QueueUserWorkItem(LoadPageWork, index);}
private void LoadPageWork(object args){    
int pageIndex = (int)args;    IList page = FetchPage(pageIndex);
SynchronizationContext.Send(LoadPageCompleted, new object[]{pageIndex, page});}
private void LoadPageCompleted(object args){int pageIndex=(int)((object[]) args)[0];
IList page = (IList)((object[])args)[1];    PopulatePage(pageIndex, page);
IsLoading = false;    FireCollectionReset();}

頁數據的加載遵循相同的慣例,再一次調用FireCollectionReset方法更新用戶UI。

也要注意IsLoading屬性是一個簡單的標識,可以用來告知UI集合正在加載。當IsLoading改變后,由INotifyPropertyChanged機制調用FirePropertyChanged方法更新UI。

public bool IsLoading{ get{ return _isLoading; }   
set {if ( value != _isLoading ){  _isLoading = value;
FirePropertyChanged("IsLoading");}    }}

演示項目

為了演示這個解決方案,我創建了一個簡單的示例項目(包括附加的源代碼項目)。

首先,創建一個IItemsProvider的一個實現,它通過使用線程休眠來模擬網絡或磁盤行為的延遲提供虛擬數據。

public class DemoCustomerProvider : IItemsProvider
{
private readonly int _count;
private readonly int _fetchDelay;

public DemoCustomerProvider(int count, int fetchDelay)
{
_count = count;
_fetchDelay = fetchDelay;
}

public int FetchCount()
{
Thread.Sleep(_fetchDelay);
return _count;
}

public IList FetchRange(int startIndex, int count)
{
Thread.Sleep(_fetchDelay);

List list = new List();
for( int i=startIndex; i {
Customer customer = new Customer {Id = i+1, Name = "Customer " + (i+1)};
list.Add(customer);
}
return list;
}
}

普遍存在的Customer(消費者)對象作為集合中的項。

為了允許用戶試驗不同的列表實現,創建一個包含ListView的簡單WPF窗體。

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Data Virtualization Demo - By Paul McClean" Height="600" Width="600">

















TextAlignment="Right" VerticalAlignment="Center"/>
Text="1000000" Width="60" VerticalAlignment="Center"/>
TextAlignment="Right" VerticalAlignment="Center"/>
Text="1000" Width="60" VerticalAlignment="Center"/>






TextAlignment="Right" VerticalAlignment="Center"/>
Margin="5" Content="List(T)" VerticalAlignment="Center"/>
Margin="5" Content="VirtualizingList(T)"
VerticalAlignment="Center"/>
Margin="5" Content="AsyncVirtualizingList(T)"
IsChecked="True" VerticalAlignment="Center"/>


TextAlignment="Right" VerticalAlignment="Center"/>
Text="100" Width="60" VerticalAlignment="Center"/>
TextAlignment="Right" VerticalAlignment="Center"/>
Text="30" Width="60" VerticalAlignment="Center"/>





VerticalAlignment="Center"/>
Width="80" VerticalAlignment="Center"/>

責任編輯:彭凡 來源: 博客園
相關推薦

2010-03-11 16:06:34

云計算服務

2015-03-18 10:35:13

虛擬化監測虛擬化策略虛擬化解決方案

2010-09-06 09:31:12

PPP數據幀

2018-05-08 15:16:59

內存虛擬化處理器

2018-04-17 15:03:40

CPU虛擬化半虛擬化

2023-06-28 15:12:33

2009-12-29 14:58:31

WPF優點

2009-06-15 16:29:42

JSONAJAX

2011-04-18 17:03:59

動態測試軟件測試

2023-10-07 11:04:58

WPF數據UI

2020-06-10 07:42:52

虛擬機容器應用場景

2023-07-10 16:01:17

云數據庫存儲

2009-07-22 17:07:16

WPF插件開發.NET Framew

2010-09-09 13:19:47

H.323協議協議棧開發

2010-04-06 09:44:40

CentOS系統

2010-10-25 16:10:20

企業虛擬化虛擬安全

2009-11-23 09:34:05

WPF本質

2024-09-29 08:40:34

2012-08-28 09:29:41

虛擬化

2010-03-01 09:16:22

Visual Stud
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久草在线视频 | 中文字幕一区二区三区四区五区 | 国产精品免费一区二区三区 | 久久精品青青大伊人av | 91久久精品一区二区二区 | 中文字幕第100页 | 超碰91在线| 国产一区二 | 第一色在线| 日韩1区2区 | 99综合| 欧美日韩久久 | 欧美精品一二区 | 久久午夜国产精品www忘忧草 | 久久久精彩视频 | 国产伦一区二区三区视频 | 91精品国产综合久久久亚洲 | 国产精品亚洲一区 | 中文在线视频 | 91福利电影在线观看 | 国产日产精品一区二区三区四区 | av特级毛片 | 最新av在线播放 | 亚洲精品一级 | 在线成人免费观看 | 亚洲成人免费av | 91精品国产综合久久久动漫日韩 | 国产亚洲二区 | 精品久久久久一区二区国产 | 日韩一区二区三区在线 | 国产日韩久久 | 成人黄色电影在线播放 | 欧美亚洲国产成人 | 日韩精品久久 | 国产精品伦一区二区三级视频 | 国产三级在线观看播放 | 欧美精品啪啪 | 精品亚洲永久免费精品 | 亚洲最大av网站 | 国产一区二区精品在线 | 日韩av在线免费 |