GacUI Demo:模擬Windows7資源管理器
阿新 • • 發佈:2018-12-27
GacUI的ListView支援Windows 7資源管理器的六種View,並且在預設的面板下表現的跟資源管理器十分類似。這個Demo也使用了一些Shell API來獲得資源管理器使用的檔案的圖示、檔案型別的字串等等。完整的程式碼可以在http://www.gaclib.net/Demos/Controls.ListView.ViewSwitching/Demo.html看到。在這裡先上圖:
Information:
Tile:
Detail:
List:
SmallIcon:
BigIcon:
想必這麼一個簡單的兩個控制元件的排版大家都已經知道怎麼寫了。首先建立一個2行1列的表格,其次直接放兩個控制元件進去。程式碼如下:
#include "..\..\Public\Source\GacUI.h"
#include <ShlObj.h>usingnamespace vl::collections;
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
externvoid FillData(GuiListView* listView);
/***********************************************************************
ViewSwitchingWindow
********************************************************************** */class ViewSwitchingWindow : public GuiWindow
{
private:
GuiListView* listView;
GuiComboBoxListControl* comboView;
void comboView_SelectedIndexChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
switch(comboView->GetSelectedIndex())
{
case0:
listView->ChangeItemStyle(new list::ListViewBigIconContentProvider);
break;
case1:
listView->ChangeItemStyle(new list::ListViewSmallIconContentProvider);
break;
case2:
listView->ChangeItemStyle(new list::ListViewListContentProvider);
break;
case3:
listView->ChangeItemStyle(new list::ListViewDetailContentProvider);
break;
case4:
listView->ChangeItemStyle(new list::ListViewTileContentProvider);
break;
case5:
listView->ChangeItemStyle(new list::ListViewInformationContentProvider);
break;
}
}
public:
ViewSwitchingWindow()
:GuiWindow(GetCurrentTheme()->CreateWindowStyle())
{
this->SetText(L"Controls.ListView.ViewSwitching");
GuiTableComposition* table=new GuiTableComposition;
table->SetCellPadding(4);
table->SetAlignmentToParent(Margin(0, 0, 0, 0));
table->SetRowsAndColumns(2, 1);
table->SetRowOption(0, GuiCellOption::MinSizeOption());
table->SetRowOption(1, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 0, 1, 1);
GuiTextList* comboSource=g::NewTextList();
comboSource->GetItems().Add(L"Big Icon");
comboSource->GetItems().Add(L"Small Icon");
comboSource->GetItems().Add(L"List");
comboSource->GetItems().Add(L"Detail");
comboSource->GetItems().Add(L"Tile");
comboSource->GetItems().Add(L"Information");
comboSource->SetHorizontalAlwaysVisible(false);
comboView=g::NewComboBox(comboSource);
comboView->SetSelectedIndex(0);
comboView->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, -1, 0));
comboView->GetBoundsComposition()->SetPreferredMinSize(Size(160, 0));
comboView->SelectedIndexChanged.AttachMethod(this, &ViewSwitchingWindow::comboView_SelectedIndexChanged);
cell->AddChild(comboView->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(1, 0, 1, 1);
listView=g::NewListViewBigIcon();
listView->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
listView->SetHorizontalAlwaysVisible(false);
listView->SetVerticalAlwaysVisible(false);
listView->SetMultiSelect(true);
cell->AddChild(listView->GetBoundsComposition());
}
this->GetBoundsComposition()->AddChild(table);
FillData(listView);
// set the preferred minimum client sizethis->GetBoundsComposition()->SetPreferredMinSize(Size(640, 480));
// call this to calculate the size immediately if any indirect content in the table changes
// so that the window can calcaulte its correct size before calling the MoveToScreenCenter()this->ForceCalculateSizeImmediately();
// move to the screen centerthis->MoveToScreenCenter();
}
};
在非虛擬模式下的ListView控制元件可以使用listView->ChangeItem(list::ListView*ContentProvider)來切換外觀。整個控制元件的設計是開放的,如果程式設計師有特別的要求的話,也可以實現一個類似的ContentProvider來控制每一個item的外觀。ContentProvider可以控制的地方有列表項的排版、座標系和每一個列表項的面板等等。排版和座標系都已經有很多預定義的類(實現)可以使用。值得一提的是,在Detail模式下的ColumnHeader是列表項的排版元件放進去的。如果沒有特別複雜的要求,單純要顯示資料的話,使用起來很簡單。上面的程式碼有一個關鍵的FillData函式,用於讀取Windows目錄(通常是C:\Windows)的檔案內容然後顯示上去。程式碼如下:
/***********************************************************************
FillData
***********************************************************************/void FillList(GuiListView* listView, const WString& path, List<WString>& files)
{
// Fill all information about a directory or a file. FOREACH(WString, file, files.Wrap())
{
Ptr<list::ListViewItem> item=new list::ListViewItem;
WString fullPath=path+L"\\"+file;
// Get large icon. item->largeImage=GetFileIcon(fullPath, SHGFI_LARGEICON | SHGFI_ICON);
// Get small icon. item->smallImage=GetFileIcon(fullPath, SHGFI_SMALLICON | SHGFI_ICON);
// Get display name item->text=GetFileDisplayName(fullPath);
// Get type name item->subItems.Add(GetFileTypeName(fullPath));
// Get last write time item->subItems.Add(GetFileLastWriteTime(fullPath));
// Get file size item->subItems.Add(GetFileSize(fullPath));
listView->GetItems().Add(item);
}
}
void FillData(GuiListView* listView)
{
// Get the Windows directory, normally L"C:\Windows". wchar_t folderPath[MAX_PATH]={0};
HRESULT hr=SHGetFolderPath(NULL, CSIDL_WINDOWS, NULL, 0, folderPath);
if(FAILED(hr)) return;
// Enumerate all directories and files in the Windows directory. List<WString> directories;
List<WString> files;
SearchDirectoriesAndFiles(folderPath, directories, files);
// Set all columns. The first column is the primary column. All others are sub columns. listView->GetItems().GetColumns().Add(new list::ListViewColumn(L"Name", 230));
listView->GetItems().GetColumns().Add(new list::ListViewColumn(L"Type", 120));
listView->GetItems().GetColumns().Add(new list::ListViewColumn(L"Date", 120));
listView->GetItems().GetColumns().Add(new list::ListViewColumn(L"Size", 120));
// Set all data columns (important sub solumns). The first sub item is 0. The primary column is not counted in. listView->GetItems().GetDataColumns().Add(0); // Type listView->GetItems().GetDataColumns().Add(1); // Data
// Fill all directories and files into the list view FillList(listView, folderPath, directories);
FillList(listView, folderPath, files);
}
/***********************************************************************
GuiMain
***********************************************************************/void GuiMain()
{
GuiWindow* window=new ViewSwitchingWindow;
GetApplication()->Run(window);
delete window;
}
跟很多GUI類庫類似,為了在ListView上面顯示內容,簡單的new一下ListViewItem和ListViewColumn,把資料都放進去就可以了。這裡的DataColumn主要是為了在Tile和Information模式下面顯示附加資料而製作的。剩下的內容就不是重點了,不過有些人可能很關心一些具體的操作,譬如怎樣獲取檔案圖示啦,怎樣獲取檔案的各種屬性等等。值得一提的是Windows有很多類似GetDateFormatEx這樣的函式,用來把幾乎所有需要在GUI上顯示的資料,轉成一個跟使用者當前的區域設定(locale)相關的字串。這種事情就應該讓作業系統來做啊。剩下的程式碼包含了很多操作Windows API獲取檔案屬性的程式碼:
/***********************************************************************
File System Operations
***********************************************************************/void SearchDirectoriesAndFiles(const WString& path, List<WString>& directories, List<WString>& files)
{
// Use FindFirstFile, FindNextFile and FindClose to enumerate all directories and files WIN32_FIND_DATA findData;
HANDLE findHandle=INVALID_HANDLE_VALUE;
while(true)
{
if(findHandle==INVALID_HANDLE_VALUE)
{
WString searchPath=path+L"\\*";
findHandle=FindFirstFile(searchPath.Buffer(), &findData);
if(findHandle==INVALID_HANDLE_VALUE)
{
break;
}
}
else
{
BOOL result=FindNextFile(findHandle, &findData);
if(result==0)
{
FindClose(findHandle);
break;
}
}
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if(wcscmp(findData.cFileName, L".")!=0&& wcscmp(findData.cFileName, L"..")!=0)
{
directories.Add(findData.cFileName);
}
}
else
{
files.Add(findData.cFileName);
}
}
Func<vint(WString a, WString b)> comparer=[](WString a, WString b){return _wcsicmp(a.Buffer(), b.Buffer());};
CopyFrom(directories.Wrap(), directories.Wrap()>>OrderBy(comparer));
CopyFrom(files.Wrap(), files.Wrap()>>OrderBy(comparer));
}
Ptr<GuiImageData> GetFileIcon(const WString& fullPath, UINT uFlags)
{
// Use SHGetFileInfo to get the correct icons for the specified directory or file. SHFILEINFO info;
DWORD result=SHGetFileInfo(fullPath.Buffer(), 0, &info, sizeof(SHFILEINFO), uFlags);
Ptr<GuiImageData> imageData;
if(result)
{
Ptr<INativeImage> image=windows::CreateImageFromHICON(info.hIcon);
if(image)
{
imageData=new GuiImageData(image, 0);
}
DestroyIcon(info.hIcon);
}
return imageData;
}
WString GetFileDisplayName(const WString& fullPath)
{
SHFILEINFO info;
DWORD result=SHGetFileInfo(fullPath.Buffer(), 0, &info, sizeof(SHFILEINFO), SHGFI_DISPLAYNAME);
return result?info.szDisplayName:L"";
}
WString GetFileTypeName(const WString& fullPath)
{
SHFILEINFO info;
DWORD result=SHGetFileInfo(fullPath.Buffer(), 0, &info, sizeof(SHFILEINFO), SHGFI_TYPENAME);
return result?info.szTypeName:L"";
}
WString GetFileLastWriteTime(const WString& fullPath)
{
// Get file attributes. WIN32_FILE_ATTRIBUTE_DATA info;
BOOL result=GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info);
// Get the localized string for the file last write date. FILETIME localFileTime;
SYSTEMTIME localSystemTime;
FileTimeToLocalFileTime(&info.ftLastWriteTime, &localFileTime);
FileTimeToSystemTime(&localFileTime, &localSystemTime);
// Get the correct locale wchar_t localeName[LOCALE_NAME_MAX_LENGTH]={0};
GetSystemDefaultLocaleName(localeName, sizeof(localeName)/sizeof(*localeName));
// Get the localized date string wchar_t dateString[100]={0};
GetDateFormatEx(localeName, DATE_SHORTDATE, &localSystemTime, NULL, dateString, sizeof(dateString)/sizeof(*dateString), NULL);
// Get the localized time string wchar_t timeString[100]={0};
GetTimeFormatEx(localeName, TIME_FORCE24HOURFORMAT | TIME_NOSECONDS, &localSystemTime, NULL, timeString, sizeof(timeString)/sizeof(*timeString));
return dateString+WString(L"")+timeString;
}
WString GetFileSize(const WString& fullPath)
{
// Get file attributes. WIN32_FILE_ATTRIBUTE_DATA info;
BOOL result=GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info);
// Get the string for file size LARGE_INTEGER li;
li.HighPart=info.nFileSizeHigh;
li.LowPart=info.nFileSizeLow;
WString unit;
double size=0;
if(li.QuadPart>=1024*1024*1024)
{
unit=L" GB";
size=(double)li.QuadPart/(1024*1024*1024);
}
elseif(li.QuadPart>=1024*1024)
{
unit=L" MB";
size=(double)li.QuadPart/(1024*1024);
}
elseif(li.QuadPart>=1024)
{
unit=L" KB";
size=(double)li.QuadPart/1024;
}
else
{
unit=L" Bytes";
size=(double)li.QuadPart;
}
WString sizeString=ftow(size);
const wchar_t* reading=sizeString.Buffer();
const wchar_t* point=wcschr(sizeString.Buffer(), L'.');
if(point)
{
const wchar_t* max=reading+sizeString.Length();
point+=4;
if(point>max) point=max;
sizeString=sizeString.Left(point-reading);
}
return sizeString+unit;
}
在這裡需要特別說明一下。這個Demo沒有使用GacUIIncludes.h,而是用GacUI.h,是因為GacUI.h包含了一些跟Windows作業系統直接相關的東西,譬如說把一個HICON型別轉成INativeImage型別的方法:windows::GetImageFromHICON。類似的操作在開發跟Windows系統本身互動比較密切的函式是很有用的。下一個Demo還沒有寫,但是基本上會選擇一個小場景來描述如何使用ListView的虛擬模式。GacUI裡面所有的列表控制元件都有虛擬模式,包括GuiVirtualTextList、GuiVirtualListView和GuiTreeView(TreeView的虛擬模式和非虛擬模式是同一個型別)等。敬請期待。
Information:
Tile:
Detail:
List:
SmallIcon:
BigIcon:
想必這麼一個簡單的兩個控制元件的排版大家都已經知道怎麼寫了。首先建立一個2行1列的表格,其次直接放兩個控制元件進去。程式碼如下:
#include
#include <ShlObj.h>usingnamespace vl::collections;
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
externvoid FillData(GuiListView* listView);
/***********************************************************************
ViewSwitchingWindow
**********************************************************************
{
private:
GuiListView* listView;
GuiComboBoxListControl* comboView;
void comboView_SelectedIndexChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
{
switch(comboView->GetSelectedIndex())
{
listView->ChangeItemStyle(new list::ListViewBigIconContentProvider);
break;
case1:
listView->ChangeItemStyle(new list::ListViewSmallIconContentProvider);
break;
case2:
listView->ChangeItemStyle(new list::ListViewListContentProvider);
break;
case3:
listView->ChangeItemStyle(new list::ListViewDetailContentProvider);
break;
case4:
listView->ChangeItemStyle(new list::ListViewTileContentProvider);
break;
case5:
listView->ChangeItemStyle(new list::ListViewInformationContentProvider);
break;
}
}
public:
ViewSwitchingWindow()
:GuiWindow(GetCurrentTheme()->CreateWindowStyle())
{
this->SetText(L"Controls.ListView.ViewSwitching");
GuiTableComposition* table=new GuiTableComposition;
table->SetCellPadding(4);
table->SetAlignmentToParent(Margin(0, 0, 0, 0));
table->SetRowsAndColumns(2, 1);
table->SetRowOption(0, GuiCellOption::MinSizeOption());
table->SetRowOption(1, GuiCellOption::PercentageOption(1.0));
table->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(0, 0, 1, 1);
GuiTextList* comboSource=g::NewTextList();
comboSource->GetItems().Add(L"Big Icon");
comboSource->GetItems().Add(L"Small Icon");
comboSource->GetItems().Add(L"List");
comboSource->GetItems().Add(L"Detail");
comboSource->GetItems().Add(L"Tile");
comboSource->GetItems().Add(L"Information");
comboSource->SetHorizontalAlwaysVisible(false);
comboView=g::NewComboBox(comboSource);
comboView->SetSelectedIndex(0);
comboView->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, -1, 0));
comboView->GetBoundsComposition()->SetPreferredMinSize(Size(160, 0));
comboView->SelectedIndexChanged.AttachMethod(this, &ViewSwitchingWindow::comboView_SelectedIndexChanged);
cell->AddChild(comboView->GetBoundsComposition());
}
{
GuiCellComposition* cell=new GuiCellComposition;
table->AddChild(cell);
cell->SetSite(1, 0, 1, 1);
listView=g::NewListViewBigIcon();
listView->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
listView->SetHorizontalAlwaysVisible(false);
listView->SetVerticalAlwaysVisible(false);
listView->SetMultiSelect(true);
cell->AddChild(listView->GetBoundsComposition());
}
this->GetBoundsComposition()->AddChild(table);
FillData(listView);
// set the preferred minimum client sizethis->GetBoundsComposition()->SetPreferredMinSize(Size(640, 480));
// call this to calculate the size immediately if any indirect content in the table changes
// so that the window can calcaulte its correct size before calling the MoveToScreenCenter()this->ForceCalculateSizeImmediately();
// move to the screen centerthis->MoveToScreenCenter();
}
};
在非虛擬模式下的ListView控制元件可以使用listView->ChangeItem(list::ListView*ContentProvider)來切換外觀。整個控制元件的設計是開放的,如果程式設計師有特別的要求的話,也可以實現一個類似的ContentProvider來控制每一個item的外觀。ContentProvider可以控制的地方有列表項的排版、座標系和每一個列表項的面板等等。排版和座標系都已經有很多預定義的類(實現)可以使用。值得一提的是,在Detail模式下的ColumnHeader是列表項的排版元件放進去的。如果沒有特別複雜的要求,單純要顯示資料的話,使用起來很簡單。上面的程式碼有一個關鍵的FillData函式,用於讀取Windows目錄(通常是C:\Windows)的檔案內容然後顯示上去。程式碼如下:
/***********************************************************************
FillData
***********************************************************************/void FillList(GuiListView* listView, const WString& path, List<WString>& files)
{
// Fill all information about a directory or a file. FOREACH(WString, file, files.Wrap())
{
Ptr<list::ListViewItem> item=new list::ListViewItem;
WString fullPath=path+L"\\"+file;
// Get large icon. item->largeImage=GetFileIcon(fullPath, SHGFI_LARGEICON | SHGFI_ICON);
// Get small icon. item->smallImage=GetFileIcon(fullPath, SHGFI_SMALLICON | SHGFI_ICON);
// Get display name item->text=GetFileDisplayName(fullPath);
// Get type name item->subItems.Add(GetFileTypeName(fullPath));
// Get last write time item->subItems.Add(GetFileLastWriteTime(fullPath));
// Get file size item->subItems.Add(GetFileSize(fullPath));
listView->GetItems().Add(item);
}
}
void FillData(GuiListView* listView)
{
// Get the Windows directory, normally L"C:\Windows". wchar_t folderPath[MAX_PATH]={0};
HRESULT hr=SHGetFolderPath(NULL, CSIDL_WINDOWS, NULL, 0, folderPath);
if(FAILED(hr)) return;
// Enumerate all directories and files in the Windows directory. List<WString> directories;
List<WString> files;
SearchDirectoriesAndFiles(folderPath, directories, files);
// Set all columns. The first column is the primary column. All others are sub columns. listView->GetItems().GetColumns().Add(new list::ListViewColumn(L"Name", 230));
listView->GetItems().GetColumns().Add(new list::ListViewColumn(L"Type", 120));
listView->GetItems().GetColumns().Add(new list::ListViewColumn(L"Date", 120));
listView->GetItems().GetColumns().Add(new list::ListViewColumn(L"Size", 120));
// Set all data columns (important sub solumns). The first sub item is 0. The primary column is not counted in. listView->GetItems().GetDataColumns().Add(0); // Type listView->GetItems().GetDataColumns().Add(1); // Data
// Fill all directories and files into the list view FillList(listView, folderPath, directories);
FillList(listView, folderPath, files);
}
/***********************************************************************
GuiMain
***********************************************************************/void GuiMain()
{
GuiWindow* window=new ViewSwitchingWindow;
GetApplication()->Run(window);
delete window;
}
跟很多GUI類庫類似,為了在ListView上面顯示內容,簡單的new一下ListViewItem和ListViewColumn,把資料都放進去就可以了。這裡的DataColumn主要是為了在Tile和Information模式下面顯示附加資料而製作的。剩下的內容就不是重點了,不過有些人可能很關心一些具體的操作,譬如怎樣獲取檔案圖示啦,怎樣獲取檔案的各種屬性等等。值得一提的是Windows有很多類似GetDateFormatEx這樣的函式,用來把幾乎所有需要在GUI上顯示的資料,轉成一個跟使用者當前的區域設定(locale)相關的字串。這種事情就應該讓作業系統來做啊。剩下的程式碼包含了很多操作Windows API獲取檔案屬性的程式碼:
/***********************************************************************
File System Operations
***********************************************************************/void SearchDirectoriesAndFiles(const WString& path, List<WString>& directories, List<WString>& files)
{
// Use FindFirstFile, FindNextFile and FindClose to enumerate all directories and files WIN32_FIND_DATA findData;
HANDLE findHandle=INVALID_HANDLE_VALUE;
while(true)
{
if(findHandle==INVALID_HANDLE_VALUE)
{
WString searchPath=path+L"\\*";
findHandle=FindFirstFile(searchPath.Buffer(), &findData);
if(findHandle==INVALID_HANDLE_VALUE)
{
break;
}
}
else
{
BOOL result=FindNextFile(findHandle, &findData);
if(result==0)
{
FindClose(findHandle);
break;
}
}
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if(wcscmp(findData.cFileName, L".")!=0&& wcscmp(findData.cFileName, L"..")!=0)
{
directories.Add(findData.cFileName);
}
}
else
{
files.Add(findData.cFileName);
}
}
Func<vint(WString a, WString b)> comparer=[](WString a, WString b){return _wcsicmp(a.Buffer(), b.Buffer());};
CopyFrom(directories.Wrap(), directories.Wrap()>>OrderBy(comparer));
CopyFrom(files.Wrap(), files.Wrap()>>OrderBy(comparer));
}
Ptr<GuiImageData> GetFileIcon(const WString& fullPath, UINT uFlags)
{
// Use SHGetFileInfo to get the correct icons for the specified directory or file. SHFILEINFO info;
DWORD result=SHGetFileInfo(fullPath.Buffer(), 0, &info, sizeof(SHFILEINFO), uFlags);
Ptr<GuiImageData> imageData;
if(result)
{
Ptr<INativeImage> image=windows::CreateImageFromHICON(info.hIcon);
if(image)
{
imageData=new GuiImageData(image, 0);
}
DestroyIcon(info.hIcon);
}
return imageData;
}
WString GetFileDisplayName(const WString& fullPath)
{
SHFILEINFO info;
DWORD result=SHGetFileInfo(fullPath.Buffer(), 0, &info, sizeof(SHFILEINFO), SHGFI_DISPLAYNAME);
return result?info.szDisplayName:L"";
}
WString GetFileTypeName(const WString& fullPath)
{
SHFILEINFO info;
DWORD result=SHGetFileInfo(fullPath.Buffer(), 0, &info, sizeof(SHFILEINFO), SHGFI_TYPENAME);
return result?info.szTypeName:L"";
}
WString GetFileLastWriteTime(const WString& fullPath)
{
// Get file attributes. WIN32_FILE_ATTRIBUTE_DATA info;
BOOL result=GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info);
// Get the localized string for the file last write date. FILETIME localFileTime;
SYSTEMTIME localSystemTime;
FileTimeToLocalFileTime(&info.ftLastWriteTime, &localFileTime);
FileTimeToSystemTime(&localFileTime, &localSystemTime);
// Get the correct locale wchar_t localeName[LOCALE_NAME_MAX_LENGTH]={0};
GetSystemDefaultLocaleName(localeName, sizeof(localeName)/sizeof(*localeName));
// Get the localized date string wchar_t dateString[100]={0};
GetDateFormatEx(localeName, DATE_SHORTDATE, &localSystemTime, NULL, dateString, sizeof(dateString)/sizeof(*dateString), NULL);
// Get the localized time string wchar_t timeString[100]={0};
GetTimeFormatEx(localeName, TIME_FORCE24HOURFORMAT | TIME_NOSECONDS, &localSystemTime, NULL, timeString, sizeof(timeString)/sizeof(*timeString));
return dateString+WString(L"")+timeString;
}
WString GetFileSize(const WString& fullPath)
{
// Get file attributes. WIN32_FILE_ATTRIBUTE_DATA info;
BOOL result=GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info);
// Get the string for file size LARGE_INTEGER li;
li.HighPart=info.nFileSizeHigh;
li.LowPart=info.nFileSizeLow;
WString unit;
double size=0;
if(li.QuadPart>=1024*1024*1024)
{
unit=L" GB";
size=(double)li.QuadPart/(1024*1024*1024);
}
elseif(li.QuadPart>=1024*1024)
{
unit=L" MB";
size=(double)li.QuadPart/(1024*1024);
}
elseif(li.QuadPart>=1024)
{
unit=L" KB";
size=(double)li.QuadPart/1024;
}
else
{
unit=L" Bytes";
size=(double)li.QuadPart;
}
WString sizeString=ftow(size);
const wchar_t* reading=sizeString.Buffer();
const wchar_t* point=wcschr(sizeString.Buffer(), L'.');
if(point)
{
const wchar_t* max=reading+sizeString.Length();
point+=4;
if(point>max) point=max;
sizeString=sizeString.Left(point-reading);
}
return sizeString+unit;
}
在這裡需要特別說明一下。這個Demo沒有使用GacUIIncludes.h,而是用GacUI.h,是因為GacUI.h包含了一些跟Windows作業系統直接相關的東西,譬如說把一個HICON型別轉成INativeImage型別的方法:windows::GetImageFromHICON。類似的操作在開發跟Windows系統本身互動比較密切的函式是很有用的。下一個Demo還沒有寫,但是基本上會選擇一個小場景來描述如何使用ListView的虛擬模式。GacUI裡面所有的列表控制元件都有虛擬模式,包括GuiVirtualTextList、GuiVirtualListView和GuiTreeView(TreeView的虛擬模式和非虛擬模式是同一個型別)等。敬請期待。