| ||
|
User Interface The Smartphone File Manager application was built for .NET CF with Visual Studio .NET 2003 and when installed in the Start Menu's Accessories folder, it looks like this: ![]() Figure 1. File Manager icon in Start Menu When the File Manager icon is selected in the Start Menu, the file manager is started with the default folder (\Storage\My Documents) opened that looks like this: ![]() Figure 2. Folder and file list The user interface is very simple and the look and feel is aligned with the standard Smartphone applications. No non-standard title bar, status bar, splitters, or other alien user interface elements. Folder navigation is done by simply selecting a subfolder or the standard "move up" (the two dots) folder. Selecting the Done soft key ends the file manager, and selecting the menu opens up the following menu options: ![]() Figure 3. Menu options The menu offers the standard file management functionality with cut, copy, paste, rename, and delete. A common function addition (borrowed from both the desktop Explorer and the Pocket PC File Manager) is the ability to paste a shortcut to a file. To use that function, you first select the file (usually an executable), select Copy in the menu, navigate to the desired target location (usually somewhere beneath \Storage\windows\Start Menu), and select Paste Shortcut. When the menu option Properties is selected, the following page is shown: ![]() Figure 4. Properties page Here the information about the file can be found, like size and creation date, and if the page is scrolled down, it looks like this: ![]() Figure 5. Properties page (continued) The attributes for the file is shown, and can also be modified. That concludes the walkthrough of the user interface, so let's move on and look at some of the code. Code When the main form (MainForm) is loaded, the following code executes: string bs = Path.DirectorySeparatorChar.ToString(); this.path = bs + "Storage" + bs + "My Documents" + bs; fillList();The default folder is set and the private method (fillList) to fill the ListView is called. The code in this method looks like this:
ListViewItem lvi;
listView.BeginUpdate();
listView.Items.Clear();
// If not root
if(this.path.Length > 1)
{
// Add "up" directory
lvi = new ListViewItem(UPDIR);
lvi.ImageIndex = 0;
listView.Items.Add(lvi);
}
// Add directories
string[] dirs = Directory.GetDirectories(this.path);
ArrayList list = new ArrayList(dirs.Length);
for(int i = 0; i < dirs.Length; i++)
list.Add(dirs[i]);
list.Sort(new SimpleComparer());
foreach(string dir in list)
{
lvi = new ListViewItem(Path.GetFileName(dir));
lvi.ImageIndex = 0;
listView.Items.Add(lvi);
}
// Add files
string[] files = Directory.GetFiles(this.path);
list = new ArrayList(files.Length);
for(int i = 0; i < files.Length; i++)
list.Add(files[i]);
list.Sort(new SimpleComparer());
foreach(string file in list)
{
lvi = new ListViewItem(Path.GetFileName(file));
lvi.ImageIndex = 1;
listView.Items.Add(lvi);
}
listView.EndUpdate();
// Select first item
listView.Items[0].Selected = true;
listView.Items[0].Focused = true;
After the list is cleared, a check is made if this is the root folder. If not, a
"move up" (the two dots) folder is added. Then all the directories are added after
they are sorted, and the same is done for the files. Finally, the first item in the
list is selected. The ListView can be navigated as usual with the keypad,
and when the Action key is pressed, the following code is run:
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex == 0;
if(lvi.Text == UPDIR)
{
this.path = this.path.Substring(0, this.path.Substring(
0, this.path.Length - 1).LastIndexOf(
Path.DirectorySeparatorChar) + 1);
fillList();
}
else if(isFolder)
{
this.path += lvi.Text + Path.DirectorySeparatorChar;
fillList();
}
else
ShellExecute.Start(this.path + lvi.Text);
The currently selected item is retrieved, and if it's the "move up" folder, the current
path is changed to the parent folder. If it's a folder, the path is changed to that
folder, and if it's a file, an attempt is made to launch the file. The file is launched
depending on how it is associated (a voice recording file is launched with Voice Notes,
and so on).
Moving on to the menu options, this is the code for the Cut menu option: ListViewItem lvi = listView.Items[listView.SelectedIndices[0]]; clipboardFileName = this.path + lvi.Text; clipboardAction = ClipboardAction.Cut;The path to the currently selected file is saved with the desired action, and the corresponding code for the Copy menu option looks like this: ListViewItem lvi = listView.Items[listView.SelectedIndices[0]]; clipboardFileName = this.path + lvi.Text; clipboardAction = ClipboardAction.Copy;Again, the current file is saved with the desired action. The more interesting stuff happen in the code for the Paste menu option:
// Check if file exist
string dest = this.path + Path.GetFileName(clipboardFileName);
if(File.Exists(dest))
{
if(MessageBox.Show("Destination already exist," +
" overwrite?", this.Text,
MessageBoxButtons.YesNo, MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2) == DialogResult.Yes)
File.Delete(dest);
else
return;
}
// Move or copy
string s = this.path.Substring(0, this.path.Length - 1);
switch(clipboardAction)
{
case ClipboardAction.Cut:
File.Move(clipboardFileName, dest);
break;
case ClipboardAction.Copy:
File.Copy(clipboardFileName, dest, false);
break;
}
clipboardAction = ClipboardAction.None;
clipboardFileName = string.Empty;
fillList();
If the destination file exist, a confirmation is required before the file is
overwritten (actually, it is first deleted), and then the desired action (cut or
copy) is performed. The code to Paste a Shortcut looks like this:
int i = 2;
string s = string.Empty;
string dest;
while(true)
{
dest = this.path + "Shortcut" + s + " to " + Path.GetFileName(Path.GetFileNameWithoutExtension(clipboardFileName) + ".lnk");
if(!File.Exists(dest))
break;
s = " (" + i.ToString() + ")";
i++;
}
StreamWriter sw = new StreamWriter(dest);
s = clipboardFileName;
if(s.IndexOf(" ") > 0)
s = "\"" + s + "\"";
s = s.Length.ToString() + "#" + s;
sw.WriteLine(s);
sw.Close();
fillList();
The shortcut filename is iterated until it becomes unique, and then the shortcut
file is written. The code for the Rename menu option looks like this:
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex == 0;
string s;
if(isFolder)
s = "Folder";
else
s = "File";
NameForm nameForm = new NameForm(this, "Rename " + s,
lvi.Text, new SetNameDelegate(SetRename));
if(nameForm.ShowDialog() == DialogResult.OK)
fillList();
The currently selected item is retrieved, and depending on if it's a file or a
folder, the corresponding caption is passed to the rename form (NameForm).
To that form is also passed a delegate that it will use to send back the new
name. The code for the delegate method (SetRename) method looks like this
(name is a string parameter):
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex == 0;
string itemName = this.path + lvi.Text;
string destName = Path.GetDirectoryName(itemName) +
Path.DirectorySeparatorChar.ToString() + name;
if(isFolder)
Directory.Move(itemName, destName);
else
File.Move(itemName, destName);
After the selected item is retrieved, it is renamed (actually Moved)
depending on if it's a file or a folder. The menu option to Delete a file
is implemented with this code:
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex == 0;
string s = "Are you sure you want to delete " + lvi.Text;
if(isFolder)
s += " and all its content";
s += "?";
if(MessageBox.Show(s, this.Text,
MessageBoxButtons.YesNo, MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2) == DialogResult.Yes)
{
if(isFolder)
Directory.Delete(this.path + lvi.Text, true);
else
File.Delete(this.path + lvi.Text);
fillList();
}
When the selected item is retrieved, a confirmation is required before the file or
folder is deleted. To create a New Folder, the following code is run:
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
NameForm nameForm = new NameForm(this, "New Folder", "",
new SetNameDelegate(SetNewName));
if(nameForm.ShowDialog() == DialogResult.OK)
fillList();
listView.Focus();
The currently selected item is retrieved, and the name form (NameForm) is
shown with the title "New Folder". To that form is also passed a delegate that it
will use to send back the new name. The code for the delegate method
(SetNewName) method looks like this (name is a string
parameter):
Directory.CreateDirectory(this.path + name);The directory is created with the name specified. Let's have a look at the code for the Properties menu option:
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
FileInfo fi = new FileInfo(this.path + lvi.Text);
PropertiesForm propertiesForm = new PropertiesForm(this, fi,
new SetNameDelegate(SetRename),
new SetAttributesDelegate(SetAttributes));
if(propertiesForm.ShowDialog() == DialogResult.OK)
fillList();
listView.Focus();
The currently selected item is retrieved, and the properties form
(PropertiesForm) is passed the information (FileInfo) about the item,
and two delegates that it will use to send back a changed name and update the file
attributes. The first delegate method is the same as is used by the rename
functionality (see above). The code for the second delegate method
(SetAttributes) method looks like this (fileAttributes is a parameter
of type FileAttributes):
ListViewItem lvi = listView.Items[listView.SelectedIndices[0]];
bool isFolder = lvi.ImageIndex == 0;
if(isFolder)
{
DirectoryInfo di = new DirectoryInfo(this.path + lvi.Text);
di.Attributes = fileAttributes;
}
else
{
FileInfo fi = new FileInfo(this.path + lvi.Text);
fi.Attributes = fileAttributes;
}
Depending on if it's a file or a folder, the attributes are set.
To finish off, let's look at some of the tweaking needed. Before the main form is shown, the following line of code is run: ListViewHelper.SetGradient(listView);This line will activate the gradient background pattern on the ListView as seen in other Smartphone applications. To do this, some P/Invoking is needed:
public static void SetGradient(ListView listView)
{
listView.Capture = true;
IntPtr hList = GetCapture();
SendMessage(hList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0,
LVS_EX_GRADIENT);
listView.Capture = false;
listView.Refresh();
}
// Native API stuff
private const uint LVM_SETEXTENDEDLISTVIEWSTYLE = 0x1000 + 54;
private const int LVS_EX_GRADIENT = 0x20000000;
[DllImport("coredll.dll")]
private static extern IntPtr GetCapture();
[DllImport("coredll.dll")]
private static extern int SendMessage(IntPtr hWnd,
uint wMsg, int wParam, int lParam);
After the window handle of the ListView is retrieved, a message is sent to the
ListView window to activate the gradient background fill.
For a more complete example, see the sample source code. Conclusion The power of the .NET Compact Framework is truly shown when creating simple but powerful applications like this. The built-in functionality for file management (in the System.IO namespace) provide the required functionality, and the user interface elements only need minimal tweaking to make the user interface conform completely with the recommendations. Any comments? |
||||||||||||||||||||||||||
| ©2001-2009 Christian Forsberg & Andreas Sjöström |
||