GacUI Demo:模拟Windows7资源管理器
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列的表格,其次直接放两个控件进去。代码如下:
在非虚拟模式下的ListView控件可以使用listView->ChangeItem(list::ListView*ContentProvider)来切换外观。整个控件的设计是开放的,如果程序员有特别的要求的话,也可以实现一个类似的ContentProvider来控制每一个item的外观。ContentProvider可以控制的地方有列表项的排版、坐标系和每一个列表项的皮肤等等。排版和坐标系都已经有很多预定义的类(实现)可以使用。值得一提的是,在Detail模式下的ColumnHeader是列表项的排版组件放进去的。如果没有特别复杂的要求,单纯要显示数据的话,使用起来很简单。上面的代码有一个关键的FillData函数,用于读取Windows目录(通常是C:\Windows)的文件内容然后显示上去。代码如下:
跟很多GUI类库类似,为了在ListView上面显示内容,简单的new一下ListViewItem和ListViewColumn,把数据都放进去就可以了。这里的DataColumn主要是为了在Tile和Information模式下面显示附加数据而制作的。剩下的内容就不是重点了,不过有些人可能很关心一些具体的操作,譬如怎样获取文件图标啦,怎样获取文件的各种属性等等。值得一提的是Windows有很多类似GetDateFormatEx这样的函数,用来把几乎所有需要在GUI上显示的数据,转成一个跟用户当前的区域设置(locale)相关的字符串。这种事情就应该让操作系统来做啊。剩下的代码包含了很多操作Windows API获取文件属性的代码:
在这里需要特别说明一下。这个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
"
..\..\Public\Source\GacUI.h
"
#include < ShlObj.h >
using namespace vl::collections;
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
extern void FillData(GuiListView * listView);
/* **********************************************************************
ViewSwitchingWindow
********************************************************************** */
class ViewSwitchingWindow : public GuiWindow
{
private :
GuiListView * listView;
GuiComboBoxListControl * comboView;
void comboView_SelectedIndexChanged(GuiGraphicsComposition * sender, GuiEventArgs & arguments)
{
switch (comboView -> GetSelectedIndex())
{
case 0 :
listView -> ChangeItemStyle( new list::ListViewBigIconContentProvider);
break ;
case 1 :
listView -> ChangeItemStyle( new list::ListViewSmallIconContentProvider);
break ;
case 2 :
listView -> ChangeItemStyle( new list::ListViewListContentProvider);
break ;
case 3 :
listView -> ChangeItemStyle( new list::ListViewDetailContentProvider);
break ;
case 4 :
listView -> ChangeItemStyle( new list::ListViewTileContentProvider);
break ;
case 5 :
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 size
this -> 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 center
this -> MoveToScreenCenter();
}
};
#include < ShlObj.h >
using namespace vl::collections;
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
extern void FillData(GuiListView * listView);
/* **********************************************************************
ViewSwitchingWindow
********************************************************************** */
class ViewSwitchingWindow : public GuiWindow
{
private :
GuiListView * listView;
GuiComboBoxListControl * comboView;
void comboView_SelectedIndexChanged(GuiGraphicsComposition * sender, GuiEventArgs & arguments)
{
switch (comboView -> GetSelectedIndex())
{
case 0 :
listView -> ChangeItemStyle( new list::ListViewBigIconContentProvider);
break ;
case 1 :
listView -> ChangeItemStyle( new list::ListViewSmallIconContentProvider);
break ;
case 2 :
listView -> ChangeItemStyle( new list::ListViewListContentProvider);
break ;
case 3 :
listView -> ChangeItemStyle( new list::ListViewDetailContentProvider);
break ;
case 4 :
listView -> ChangeItemStyle( new list::ListViewTileContentProvider);
break ;
case 5 :
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 size
this -> 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 center
this -> 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;
}
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 );
}
else if (li.QuadPart >= 1024 * 1024 )
{
unit = L " MB " ;
size = ( double )li.QuadPart / ( 1024 * 1024 );
}
else if (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;
}
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 );
}
else if (li.QuadPart >= 1024 * 1024 )
{
unit = L " MB " ;
size = ( double )li.QuadPart / ( 1024 * 1024 );
}
else if (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的虚拟模式和非虚拟模式是同一个类型)等。敬请期待。