Command Prompt (2)
Keywords: Xamarin.Forms, ScrollView, ScrollToAsync, PCL Storage, Command Prompt, Entry without Border Line, Blinking Cursor
I redeveloped Command Prompt (1) that emulates MS-DOS Command-Prompt-Like UI. In this App, command lines are simply added on ScrollView, the screen design is basically same as the Command Prompt (1). However, in the Command Prompt (1), I couldn’t make ScrollView size expansion and ScrollToAsync method work well, so I modified the App with techniques that I learned afterwards. Since PCL Storage is used for folder and file handling, the title of this App can be PCL Storage (3), the new version of PCL Storage (1) and (2), as well as Command Prompt (2).
A screen shot of the iOS Simullator just after launching the App.
MainPage.xaml
The source code is also here.
Designed the screen layout with four rows and one column Grid. From the upper row, App title, some Buttons to control somthing, main area displaying the command lines, and the bottom area is to show some variables for debugging. In the main area, the ScrollVew “ConsoleBack” are placed in the Grid that surrounds the StackLayout “Console.” The other Grid “FileEditorGrid” is also overlaid in the main area that shows the file editor. The IsVisible property of the “FileEditorGrid” is false initially, so it is pop-up dilplayed when the file editor is launched.
CommandInterpreter.cs
CommandLine class defined command prompt, command input and so on. The Entry “Command Entry” gets the text of command, but it is not displayed on the screen beause its HightRequest property is zero. The input command is transferred the Label “CommandLabel” through InputChanged event. This makes it possible to realize text input box without border line.
The timer in the “AddPrompt” method is to blink “|” at the end of command line to show command input waiting.
When Return key is pressed in command line, InputCompleted event is raised and the “CommandExecute” method is called.
MainPage.xaml.cs
The “CommandExecute” method identifies which command has been executed from the first three characters of the command text. All of the commands are familiar in MS-DOS. The command of “mkdir”, “del” etc. call some methods of PCL Storage.
PCLStorage.cs
The methods for PCL Storage is not so different from the ones of PCLStorage (2). The arguments of some methods are changed. And the “Folder” and “File” class are newly defined, and they make it easier to refer some properties.
A movie showing this App running. You can get the original movie file from here (in "movie" folder). Since the PCL Storage is used in this App, folders and files are actually created in the LocalStorage of the PC in which the iOS simulator is running. So the movie also shows the LocalStorage folder.
Finally, I know that Command-Prompt-Like UI is not suitable for mobile application, but this is also my training!
I redeveloped Command Prompt (1) that emulates MS-DOS Command-Prompt-Like UI. In this App, command lines are simply added on ScrollView, the screen design is basically same as the Command Prompt (1). However, in the Command Prompt (1), I couldn’t make ScrollView size expansion and ScrollToAsync method work well, so I modified the App with techniques that I learned afterwards. Since PCL Storage is used for folder and file handling, the title of this App can be PCL Storage (3), the new version of PCL Storage (1) and (2), as well as Command Prompt (2).
A screen shot of the iOS Simullator just after launching the App.
MainPage.xaml
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:CommandPrompt"
x:Class="CommandPrompt.MainPage">
<Grid x:Name="grid" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Title -->
<StackLayout Grid.Row="0" BackgroundColor="Gray"
Padding="0,0,0,1" HeightRequest="50">
<StackLayout BackgroundColor="White" HeightRequest="50">
<Label Text="Command Prompt Demo"
Grid.Row="0" VerticalOptions="EndAndExpand"/>
</StackLayout>
</StackLayout>
<!-- Buttons to control this App -->
<StackLayout Grid.Row="1" Padding="0,0,0,1" BackgroundColor="Gray">
<StackLayout Orientation="Horizontal" BackgroundColor="White">
<Button x:Name="Button1"
Text="B1" Clicked="Button01"
WidthRequest="50" HeightRequest="25"
BorderWidth="1" Margin="3,3,0,3"/>
<Button x:Name="Button2"
Text="B2" Clicked="Button02"
WidthRequest="50" HeightRequest="25"
BorderWidth="1" Margin="3,3,0,3"/>
<Button x:Name="Button3"
Text="B3"
WidthRequest="50" HeightRequest="25"
BorderWidth="1" Margin="3,3,0,3"/>
<Label x:Name="StatusLabel" Text="Status Label"
HorizontalOptions="EndAndExpand" VerticalOptions="Center"/>
</StackLayout>
</StackLayout>
<!-- Command Prompt Console -->
<ScrollView x:Name="ConsoleBack" Grid.Row="2" BackgroundColor="Gray"
Padding="0" VerticalOptions="FillAndExpand" Scrolled="ConsoleBackScrolled">
<StackLayout x:Name="Console" BackgroundColor="White" Spacing="0"
SizeChanged="GetConsoleSize">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapConsole"/>
</StackLayout.GestureRecognizers>
</StackLayout>
</ScrollView>
<!-- File Editor -->
<Grid x:Name="FileEditorGrid" Grid.Row="2" BackgroundColor="Gray" IsVisible="false"
Padding="1" RowSpacing="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Title -->
<Label x:Name="FileEditorTitle" BackgroundColor="White"
Grid.Row="0" Grid.Column="0"/>
<!-- File Editor -->
<Editor x:Name="FileEditor" BackgroundColor="White"
VerticalOptions="FillAndExpand" Keyboard="Plain"
Grid.Row="1" Grid.Column="0"/>
<!-- Button for Save File and Cancel -->
<StackLayout BackgroundColor="White" Grid.Row="2" Grid.Column="0">
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<Button Text="Save" TextColor="Black" Clicked="SaveFile" HeightRequest="20"
BackgroundColor="White" BorderWidth="1" />
<Button Text="Cancel" TextColor="Black" Clicked="CancelEditor" HeightRequest="20"
BackgroundColor="White" BorderWidth="1" />
</StackLayout>
</StackLayout>
</Grid>
<!-- Monitor some variables for Debug -->
<StackLayout x:Name="Debug" Grid.Row="3"
BackgroundColor="Gray" Padding="0,1,0,0">
<StackLayout BackgroundColor="White">
<Label x:Name="Label1" Text=" "/>
<Label x:Name="Label2" Text=" "/>
<Label x:Name="Label3" Text=" "/>
</StackLayout>
</StackLayout>
</Grid>
</ContentPage>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:CommandPrompt"
x:Class="CommandPrompt.MainPage">
<Grid x:Name="grid" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Title -->
<StackLayout Grid.Row="0" BackgroundColor="Gray"
Padding="0,0,0,1" HeightRequest="50">
<StackLayout BackgroundColor="White" HeightRequest="50">
<Label Text="Command Prompt Demo"
Grid.Row="0" VerticalOptions="EndAndExpand"/>
</StackLayout>
</StackLayout>
<!-- Buttons to control this App -->
<StackLayout Grid.Row="1" Padding="0,0,0,1" BackgroundColor="Gray">
<StackLayout Orientation="Horizontal" BackgroundColor="White">
<Button x:Name="Button1"
Text="B1" Clicked="Button01"
WidthRequest="50" HeightRequest="25"
BorderWidth="1" Margin="3,3,0,3"/>
<Button x:Name="Button2"
Text="B2" Clicked="Button02"
WidthRequest="50" HeightRequest="25"
BorderWidth="1" Margin="3,3,0,3"/>
<Button x:Name="Button3"
Text="B3"
WidthRequest="50" HeightRequest="25"
BorderWidth="1" Margin="3,3,0,3"/>
<Label x:Name="StatusLabel" Text="Status Label"
HorizontalOptions="EndAndExpand" VerticalOptions="Center"/>
</StackLayout>
</StackLayout>
<!-- Command Prompt Console -->
<ScrollView x:Name="ConsoleBack" Grid.Row="2" BackgroundColor="Gray"
Padding="0" VerticalOptions="FillAndExpand" Scrolled="ConsoleBackScrolled">
<StackLayout x:Name="Console" BackgroundColor="White" Spacing="0"
SizeChanged="GetConsoleSize">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapConsole"/>
</StackLayout.GestureRecognizers>
</StackLayout>
</ScrollView>
<!-- File Editor -->
<Grid x:Name="FileEditorGrid" Grid.Row="2" BackgroundColor="Gray" IsVisible="false"
Padding="1" RowSpacing="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Title -->
<Label x:Name="FileEditorTitle" BackgroundColor="White"
Grid.Row="0" Grid.Column="0"/>
<!-- File Editor -->
<Editor x:Name="FileEditor" BackgroundColor="White"
VerticalOptions="FillAndExpand" Keyboard="Plain"
Grid.Row="1" Grid.Column="0"/>
<!-- Button for Save File and Cancel -->
<StackLayout BackgroundColor="White" Grid.Row="2" Grid.Column="0">
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<Button Text="Save" TextColor="Black" Clicked="SaveFile" HeightRequest="20"
BackgroundColor="White" BorderWidth="1" />
<Button Text="Cancel" TextColor="Black" Clicked="CancelEditor" HeightRequest="20"
BackgroundColor="White" BorderWidth="1" />
</StackLayout>
</StackLayout>
</Grid>
<!-- Monitor some variables for Debug -->
<StackLayout x:Name="Debug" Grid.Row="3"
BackgroundColor="Gray" Padding="0,1,0,0">
<StackLayout BackgroundColor="White">
<Label x:Name="Label1" Text=" "/>
<Label x:Name="Label2" Text=" "/>
<Label x:Name="Label3" Text=" "/>
</StackLayout>
</StackLayout>
</Grid>
</ContentPage>
The source code is also here.
Designed the screen layout with four rows and one column Grid. From the upper row, App title, some Buttons to control somthing, main area displaying the command lines, and the bottom area is to show some variables for debugging. In the main area, the ScrollVew “ConsoleBack” are placed in the Grid that surrounds the StackLayout “Console.” The other Grid “FileEditorGrid” is also overlaid in the main area that shows the file editor. The IsVisible property of the “FileEditorGrid” is false initially, so it is pop-up dilplayed when the file editor is launched.
CommandInterpreter.cs
CommandInterpreter.cs
using System;
using System.Linq;
using System.Collections.Generic;
using Xamarin.Forms;
namespace CommandPrompt
{
public partial class MainPage : ContentPage
{
class CommandLine
{
// Prompt
public string Prompt { get; set; }
private Label promptlabel()
{
return new Label() { Text = Prompt,
TextColor = Color.Blue };
}
public Label PromptLabel { get { return this.promptlabel(); } }
// Command
public string Command { get { return CommandEntry.Text; } }
private Label commandlabel = new Label() { Text = " " };
public Label CommandLabel { get { return this.commandlabel; } }
// Command Input
private Entry commandentry = new Entry()
{
BackgroundColor = Color.Transparent,
TextColor = Color.Transparent,
HeightRequest = 0, // Not to display this entry
Keyboard = Keyboard.Plain
};
public Entry CommandEntry { get { return this.commandentry; } }
// StackLayout including all parts of Command Line
private StackLayout promptcommand()
{
StackLayout s1 = new StackLayout()
{
BackgroundColor = Color.Transparent,
Orientation = StackOrientation.Horizontal,
Children = { PromptLabel, CommandLabel }
};
StackLayout s2 = new StackLayout()
{
Spacing = 0,
Children = { s1, CommandEntry }
};
return s2;
}
public StackLayout PromptCommand { get { return promptcommand(); } }
// Blink Cursor and its Switch
public string BlinkString = "|";
public bool BlinkOff { get; set; }
// Solution Results
public string Results { get; set; }
}
List<CommandLine> CommandLines = new List<CommandLine>();
int ConsoleSizeChanged = 0;
int TextInputed = 0;
int TextInputCompleted = 0;
double ConsoleWidth, ConsoleHeight;
double ScrollWidth, ScrollHeight;
//Get "Console" size properly and Scroll "Console" to the end
void GetConsoleSize(object sender, EventArgs args)
{
ConsoleWidth = Console.Width;
ConsoleHeight = Console.Height;
ScrollHeight = ConsoleBack.Height;
// Scroll "Console" to the End
// Make ScrollToAsync into Timer due to timing probelm(?)
Device.StartTimer(TimeSpan.FromMilliseconds(1), () =>
{
ConsoleBack.ScrollToAsync(0, ConsoleHeight - ScrollHeight, false);
return false; // Timer Cycle is only one time
});
}
// Add a new Command Input Line
void AddPrompt(string prompt)
{
CommandLine commandLine = new CommandLine();
commandLine.Prompt = prompt;
Label comLabel = commandLine.CommandLabel;
Entry comEntry = commandLine.CommandEntry;
Console.Children.Add(commandLine.PromptCommand);
CommandLines.Add(commandLine);
// Add TextChanged & Completed Event on entry
comEntry.TextChanged += InputChanged;
comEntry.Completed += InputCompleted;
comEntry.Focus();
// Blink Cursor in Command Line
Device.StartTimer(TimeSpan.FromMilliseconds(300), () =>
{
// If Text Inputed in Command Line, finish this Timer
bool NextTimerStart = !commandLine.BlinkOff;
if (!NextTimerStart)
{
// Not to let label be "| "
if (comEntry.Text == null) comLabel.Text = "";
// Delete BlinkString
comLabel.Text = comEntry.Text;
return NextTimerStart;
}
// Toggle Cursor to Blink
if (commandLine.BlinkString == "|") commandLine.BlinkString = "";
else commandLine.BlinkString = "|";
comLabel.Text = comEntry.Text + commandLine.BlinkString;
return NextTimerStart;
});
}
// Display the Input Command (entry.Text) as Label
void InputChanged(object sender, EventArgs args)
{
Entry entry = (Entry)sender;
CommandLines.Last().CommandLabel.Text = entry.Text;
TextInputed++;
}
// When CR is entered, Execute the Input Command
void InputCompleted(object sender, EventArgs args)
{
Entry entry = (Entry)sender;
CommandLines.Last().BlinkOff = true;
CommandExecute(entry.Text);
entry.IsEnabled = false; // Not to focus the previous entry
TextInputCompleted++;
}
void ConsoleBackScrolled(object sender, EventArgs args)
{
//Label3.Text = string.Format("Scroll Y = {0}, Scrolled Y = {1}",
// ConsoleHeight - ScrollHeight,
// ConsoleBack.ScrollY);
}
}
}
using System.Linq;
using System.Collections.Generic;
using Xamarin.Forms;
namespace CommandPrompt
{
public partial class MainPage : ContentPage
{
class CommandLine
{
// Prompt
public string Prompt { get; set; }
private Label promptlabel()
{
return new Label() { Text = Prompt,
TextColor = Color.Blue };
}
public Label PromptLabel { get { return this.promptlabel(); } }
// Command
public string Command { get { return CommandEntry.Text; } }
private Label commandlabel = new Label() { Text = " " };
public Label CommandLabel { get { return this.commandlabel; } }
// Command Input
private Entry commandentry = new Entry()
{
BackgroundColor = Color.Transparent,
TextColor = Color.Transparent,
HeightRequest = 0, // Not to display this entry
Keyboard = Keyboard.Plain
};
public Entry CommandEntry { get { return this.commandentry; } }
// StackLayout including all parts of Command Line
private StackLayout promptcommand()
{
StackLayout s1 = new StackLayout()
{
BackgroundColor = Color.Transparent,
Orientation = StackOrientation.Horizontal,
Children = { PromptLabel, CommandLabel }
};
StackLayout s2 = new StackLayout()
{
Spacing = 0,
Children = { s1, CommandEntry }
};
return s2;
}
public StackLayout PromptCommand { get { return promptcommand(); } }
// Blink Cursor and its Switch
public string BlinkString = "|";
public bool BlinkOff { get; set; }
// Solution Results
public string Results { get; set; }
}
List<CommandLine> CommandLines = new List<CommandLine>();
int ConsoleSizeChanged = 0;
int TextInputed = 0;
int TextInputCompleted = 0;
double ConsoleWidth, ConsoleHeight;
double ScrollWidth, ScrollHeight;
//Get "Console" size properly and Scroll "Console" to the end
void GetConsoleSize(object sender, EventArgs args)
{
ConsoleWidth = Console.Width;
ConsoleHeight = Console.Height;
ScrollHeight = ConsoleBack.Height;
// Scroll "Console" to the End
// Make ScrollToAsync into Timer due to timing probelm(?)
Device.StartTimer(TimeSpan.FromMilliseconds(1), () =>
{
ConsoleBack.ScrollToAsync(0, ConsoleHeight - ScrollHeight, false);
return false; // Timer Cycle is only one time
});
}
// Add a new Command Input Line
void AddPrompt(string prompt)
{
CommandLine commandLine = new CommandLine();
commandLine.Prompt = prompt;
Label comLabel = commandLine.CommandLabel;
Entry comEntry = commandLine.CommandEntry;
Console.Children.Add(commandLine.PromptCommand);
CommandLines.Add(commandLine);
// Add TextChanged & Completed Event on entry
comEntry.TextChanged += InputChanged;
comEntry.Completed += InputCompleted;
comEntry.Focus();
// Blink Cursor in Command Line
Device.StartTimer(TimeSpan.FromMilliseconds(300), () =>
{
// If Text Inputed in Command Line, finish this Timer
bool NextTimerStart = !commandLine.BlinkOff;
if (!NextTimerStart)
{
// Not to let label be "| "
if (comEntry.Text == null) comLabel.Text = "";
// Delete BlinkString
comLabel.Text = comEntry.Text;
return NextTimerStart;
}
// Toggle Cursor to Blink
if (commandLine.BlinkString == "|") commandLine.BlinkString = "";
else commandLine.BlinkString = "|";
comLabel.Text = comEntry.Text + commandLine.BlinkString;
return NextTimerStart;
});
}
// Display the Input Command (entry.Text) as Label
void InputChanged(object sender, EventArgs args)
{
Entry entry = (Entry)sender;
CommandLines.Last().CommandLabel.Text = entry.Text;
TextInputed++;
}
// When CR is entered, Execute the Input Command
void InputCompleted(object sender, EventArgs args)
{
Entry entry = (Entry)sender;
CommandLines.Last().BlinkOff = true;
CommandExecute(entry.Text);
entry.IsEnabled = false; // Not to focus the previous entry
TextInputCompleted++;
}
void ConsoleBackScrolled(object sender, EventArgs args)
{
//Label3.Text = string.Format("Scroll Y = {0}, Scrolled Y = {1}",
// ConsoleHeight - ScrollHeight,
// ConsoleBack.ScrollY);
}
}
}
CommandLine class defined command prompt, command input and so on. The Entry “Command Entry” gets the text of command, but it is not displayed on the screen beause its HightRequest property is zero. The input command is transferred the Label “CommandLabel” through InputChanged event. This makes it possible to realize text input box without border line.
The timer in the “AddPrompt” method is to blink “|” at the end of command line to show command input waiting.
When Return key is pressed in command line, InputCompleted event is raised and the “CommandExecute” method is called.
MainPage.xaml.cs
MainPage.xaml.cs
using System;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace CommandPrompt
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
CreateRootFolder();
AddPrompt(CurrentFolder.Path);
// Just after launching the App, Entry.Focus() doesn't work like
// ScrollToAsync() due to timing probelm(?), so maake it into Timer
Device.StartTimer(TimeSpan.FromMilliseconds(1), () =>
{
CommandLines.Last().CommandEntry.Focus();
return false; // Timer Cycle is only one time
});
}
// Focus Command Line, if Tap Console
void OnTapConsole(object sender, EventArgs args)
{
CommandLines.Last().CommandEntry.Focus();
}
// Identify Input Command and Execute it
async void CommandExecute(string command)
{
string result = "";
// Get the first three characters of command
string com = command;
if(command != null)
if(command.Length>3)
com = command.Substring(0, 3);
// Identify the command from the first three characters
switch (com)
{
case "":
case null:
AddPrompt(CurrentFolder.Path);
return;
case "dir":
result = dir();
break;
case "pwd":
result = CurrentFolder.Path + "\n";
break;
case "mkd":
result = await mkdir(command);
break;
case "cd ":
result = await cd(command);
break;
case "del":
result = await del(command);
break;
case "mor":
result = await more(command);
break;
case "ed ":
result = await ed(command);
break;
default:
result = "No Command" + "\n";
break;
}
// Display the Command Execute Result following the Command Line
Console.Children.Add(new Label() { Text = result });
// Store the result
CommandLines.Last().Results = result;
// Add a new Command Line
AddPrompt(CurrentFolder.Path);
}
// Display All Folders and Files
string dir()
{
string result = "";
Folder folder = CurrentFolder;
foreach(Folder subfolder in folder.SubFolders)
result += string.Format("<DIR> {0}\n", subfolder.Name);
foreach(File file in folder.Files)
result += file.Name + "\n";
return result;
}
// Make Directory
async Task<string> mkdir(string command)
{
string result = " ";
string name = "";
Folder parent = CurrentFolder;
if (command.Length < 6) return "Command Invalid";
else if (command.Substring(0, 6) != "mkdir ")
return "Command Invalid";
else if (command.Substring(6) == "")
return "No Folder Name";
else name = command.Substring(6);
Folder folder = new Folder()
{
Name = name,
Parent = parent
};
// Check the existence of the IFolder corresponding to the Object "folder"
bool IsExist = await ICheckFolderExist(name, parent.iFolder);
if (IsExist) return name.ToString() + " already exists";
folder.iFolder = await ICreateFolder(folder, parent.iFolder, false);
parent.SubFolders.Add(folder);
//Label3.Text = folder.iFolder.Name;
return result;
}
// Change Directory
async Task<string> cd(string command)
{
string result = " ";
string name = "";
if (command.Substring(3) == "")
return "No Folder Name";
else name = command.Substring(3);
// Back to the Parent Folder
if(name == "..")
{
if (CurrentFolder == LocalStorage) return "No Parent Folder";
CurrentFolder = CurrentFolder.Parent;
return result;
}
// Check the existence of the IFolder corresponding to the Object "folder"
bool IsExist = await ICheckFolderExist(name, CurrentFolder.iFolder);
if (!IsExist) return name + " doesn't exist";
foreach(Folder folder in CurrentFolder.SubFolders)
{
if (folder.Name == name)
{
CurrentFolder = folder;
break;
}
}
return result;
}
// Delete a Folder or a File
async Task<string> del(string command)
{
string result = " ";
string name = "";
if (command.Substring(0, 4) != "del ")
return "Command Invalid";
else if (command.Substring(4) == "")
return "No Folder/File Name";
else name = command.Substring(4);
// Check the existence of the IFolder or the IFile
bool IsExist1 = await ICheckFolderExist(name, CurrentFolder.iFolder);
bool IsExist2 = await ICheckFileExist(name, CurrentFolder.iFolder);
if(IsExist1) // Delete Folder
{
Folder DelFolder = new Folder();
foreach(Folder folder in CurrentFolder.SubFolders)
{
if(folder.Name==name)
{
await IDeleteFolder(folder.iFolder, folder.IParent);
DelFolder = folder;
break;
}
}
CurrentFolder.SubFolders.Remove(DelFolder);
}
else if(IsExist2) // Delete File
{
File DelFile = new File();
foreach (File file in CurrentFolder.Files)
{
if (file.Name == name)
{
await IDeleteFile(file.iFile, file.IParent);
DelFile = file;
break;
}
}
CurrentFolder.Files.Remove(DelFile);
}
else return name + " doesn't exist";
return result;
}
// Display File Content
async Task<string> more(string command)
{
//string result = " ";
string name = "";
Folder parent = CurrentFolder;
if (command.Length < 5) return "Command Invalid";
else if (command.Substring(0, 5) != "more ")
return "Command Invalid";
else if (command.Substring(5) == "")
return "No File Name";
else name = command.Substring(5);
foreach (File file in parent.Files)
if (file.Name == name)
return await IReadFile(file.iFile);
return "No such File";
}
// Edit File
async Task<string> ed(string command)
{
string result = " ";
string name = "";
Folder parent = CurrentFolder;
if (command.Substring(3) == "")
return "No File Name";
else name = command.Substring(3);
// Check the existence of the File
bool IsExist = await ICheckFileExist(name, parent.iFolder);
if(IsExist)
{
foreach(File file in parent.Files)
{
if (file.Name == name)
{
FileEditor.Text = await IReadFile(file.iFile);
CurrentFile = file;
break;
}
}
}
else
{
File file = new File()
{
Name = name,
Parent = parent
};
file.iFile = await ICreateFile(file, parent.iFolder);
parent.Files.Add(file);
CurrentFile = file;
}
FileEditorTitle.Text = "File Name: " + CurrentFile.Name;
FileEditorGrid.IsVisible = true;
// Editor.Focus() doesn't work due to timing probelm(?),
// so maake it into Timer
Device.StartTimer(TimeSpan.FromMilliseconds(1), () =>
{
FileEditor.Focus();
return false; // Timer Cycle is only one time
});
return result;
}
// Buttons of FileEditor Menu: save File, cancel Editor
void SaveFile(object sender, EventArgs e)
{
// PCL Storage: Write content in IFile
IWriteFile(CurrentFile.iFile, FileEditor.Text);
}
void CancelEditor(object sender, EventArgs e)
{
FileEditorGrid.IsVisible = false;
CommandLines.Last().CommandEntry.Focus();
}
void Button01(object sender, EventArgs args)
{
}
void Button02(object sender, EventArgs args)
{
IDleteAllObjects(LocalStorage);
CommandLines.Last().CommandEntry.Focus();
}
}
}
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace CommandPrompt
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
CreateRootFolder();
AddPrompt(CurrentFolder.Path);
// Just after launching the App, Entry.Focus() doesn't work like
// ScrollToAsync() due to timing probelm(?), so maake it into Timer
Device.StartTimer(TimeSpan.FromMilliseconds(1), () =>
{
CommandLines.Last().CommandEntry.Focus();
return false; // Timer Cycle is only one time
});
}
// Focus Command Line, if Tap Console
void OnTapConsole(object sender, EventArgs args)
{
CommandLines.Last().CommandEntry.Focus();
}
// Identify Input Command and Execute it
async void CommandExecute(string command)
{
string result = "";
// Get the first three characters of command
string com = command;
if(command != null)
if(command.Length>3)
com = command.Substring(0, 3);
// Identify the command from the first three characters
switch (com)
{
case "":
case null:
AddPrompt(CurrentFolder.Path);
return;
case "dir":
result = dir();
break;
case "pwd":
result = CurrentFolder.Path + "\n";
break;
case "mkd":
result = await mkdir(command);
break;
case "cd ":
result = await cd(command);
break;
case "del":
result = await del(command);
break;
case "mor":
result = await more(command);
break;
case "ed ":
result = await ed(command);
break;
default:
result = "No Command" + "\n";
break;
}
// Display the Command Execute Result following the Command Line
Console.Children.Add(new Label() { Text = result });
// Store the result
CommandLines.Last().Results = result;
// Add a new Command Line
AddPrompt(CurrentFolder.Path);
}
// Display All Folders and Files
string dir()
{
string result = "";
Folder folder = CurrentFolder;
foreach(Folder subfolder in folder.SubFolders)
result += string.Format("<DIR> {0}\n", subfolder.Name);
foreach(File file in folder.Files)
result += file.Name + "\n";
return result;
}
// Make Directory
async Task<string> mkdir(string command)
{
string result = " ";
string name = "";
Folder parent = CurrentFolder;
if (command.Length < 6) return "Command Invalid";
else if (command.Substring(0, 6) != "mkdir ")
return "Command Invalid";
else if (command.Substring(6) == "")
return "No Folder Name";
else name = command.Substring(6);
Folder folder = new Folder()
{
Name = name,
Parent = parent
};
// Check the existence of the IFolder corresponding to the Object "folder"
bool IsExist = await ICheckFolderExist(name, parent.iFolder);
if (IsExist) return name.ToString() + " already exists";
folder.iFolder = await ICreateFolder(folder, parent.iFolder, false);
parent.SubFolders.Add(folder);
//Label3.Text = folder.iFolder.Name;
return result;
}
// Change Directory
async Task<string> cd(string command)
{
string result = " ";
string name = "";
if (command.Substring(3) == "")
return "No Folder Name";
else name = command.Substring(3);
// Back to the Parent Folder
if(name == "..")
{
if (CurrentFolder == LocalStorage) return "No Parent Folder";
CurrentFolder = CurrentFolder.Parent;
return result;
}
// Check the existence of the IFolder corresponding to the Object "folder"
bool IsExist = await ICheckFolderExist(name, CurrentFolder.iFolder);
if (!IsExist) return name + " doesn't exist";
foreach(Folder folder in CurrentFolder.SubFolders)
{
if (folder.Name == name)
{
CurrentFolder = folder;
break;
}
}
return result;
}
// Delete a Folder or a File
async Task<string> del(string command)
{
string result = " ";
string name = "";
if (command.Substring(0, 4) != "del ")
return "Command Invalid";
else if (command.Substring(4) == "")
return "No Folder/File Name";
else name = command.Substring(4);
// Check the existence of the IFolder or the IFile
bool IsExist1 = await ICheckFolderExist(name, CurrentFolder.iFolder);
bool IsExist2 = await ICheckFileExist(name, CurrentFolder.iFolder);
if(IsExist1) // Delete Folder
{
Folder DelFolder = new Folder();
foreach(Folder folder in CurrentFolder.SubFolders)
{
if(folder.Name==name)
{
await IDeleteFolder(folder.iFolder, folder.IParent);
DelFolder = folder;
break;
}
}
CurrentFolder.SubFolders.Remove(DelFolder);
}
else if(IsExist2) // Delete File
{
File DelFile = new File();
foreach (File file in CurrentFolder.Files)
{
if (file.Name == name)
{
await IDeleteFile(file.iFile, file.IParent);
DelFile = file;
break;
}
}
CurrentFolder.Files.Remove(DelFile);
}
else return name + " doesn't exist";
return result;
}
// Display File Content
async Task<string> more(string command)
{
//string result = " ";
string name = "";
Folder parent = CurrentFolder;
if (command.Length < 5) return "Command Invalid";
else if (command.Substring(0, 5) != "more ")
return "Command Invalid";
else if (command.Substring(5) == "")
return "No File Name";
else name = command.Substring(5);
foreach (File file in parent.Files)
if (file.Name == name)
return await IReadFile(file.iFile);
return "No such File";
}
// Edit File
async Task<string> ed(string command)
{
string result = " ";
string name = "";
Folder parent = CurrentFolder;
if (command.Substring(3) == "")
return "No File Name";
else name = command.Substring(3);
// Check the existence of the File
bool IsExist = await ICheckFileExist(name, parent.iFolder);
if(IsExist)
{
foreach(File file in parent.Files)
{
if (file.Name == name)
{
FileEditor.Text = await IReadFile(file.iFile);
CurrentFile = file;
break;
}
}
}
else
{
File file = new File()
{
Name = name,
Parent = parent
};
file.iFile = await ICreateFile(file, parent.iFolder);
parent.Files.Add(file);
CurrentFile = file;
}
FileEditorTitle.Text = "File Name: " + CurrentFile.Name;
FileEditorGrid.IsVisible = true;
// Editor.Focus() doesn't work due to timing probelm(?),
// so maake it into Timer
Device.StartTimer(TimeSpan.FromMilliseconds(1), () =>
{
FileEditor.Focus();
return false; // Timer Cycle is only one time
});
return result;
}
// Buttons of FileEditor Menu: save File, cancel Editor
void SaveFile(object sender, EventArgs e)
{
// PCL Storage: Write content in IFile
IWriteFile(CurrentFile.iFile, FileEditor.Text);
}
void CancelEditor(object sender, EventArgs e)
{
FileEditorGrid.IsVisible = false;
CommandLines.Last().CommandEntry.Focus();
}
void Button01(object sender, EventArgs args)
{
}
void Button02(object sender, EventArgs args)
{
IDleteAllObjects(LocalStorage);
CommandLines.Last().CommandEntry.Focus();
}
}
}
The “CommandExecute” method identifies which command has been executed from the first three characters of the command text. All of the commands are familiar in MS-DOS. The command of “mkdir”, “del” etc. call some methods of PCL Storage.
PCLStorage.cs
PCLStorage.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
using PCLStorage;
namespace CommandPrompt
{
public partial class MainPage : ContentPage
{
// Set LocalStorage
// Name: ILocalStorage.Name, Path: ILocalStorage.Path
static IFolder ILocalStorage = FileSystem.Current.LocalStorage;
// Create Folder class of LocalStorage and RootFolder
static Folder LocalStorage = new Folder()
{
Name = "LocalStorage",
Parent = null,
iFolder = ILocalStorage
};
static Folder RootFolder = new Folder()
{
Name = "RootFolder",
Parent = LocalStorage
};
Folder CurrentFolder = RootFolder;
// Folsder class
class Folder
{
public string Name { get; set; } // Folder Name
public Folder Parent { get; set; } // Parent Folder
private string path()
{
if (Name == "LocalStorage") return "LocalStorage>";
else if (Name == "RootFolder") return ">";
else if (Parent == RootFolder) return Name + ">";
else return Parent.Path + Name + ">";
}
public string Path { get { return path(); } } // Directory Path
// SubFolder and File List in this Folder
public List<Folder> SubFolders = new List<Folder>();
public List<File> Files = new List<File>();
public int NumChildren // Number of Children in this folder
{
get { return SubFolders.Count + Files.Count; }
}
public string NumChildrenText // Text of NoChildren
{
get { return "(" + NumChildren.ToString() + ")"; }
}
// IFolder of this Folder and its Parent Folder
public IFolder iFolder { get; set; }
public IFolder IParent { get { return Parent.iFolder; } } // Parent IFolder
}
// File class
class File
{
public string Name { get; set; } // File Name
public Folder Parent { get; set; } // Parent Folder
public string Path { get { return Parent.Path; } } // Directory Path
// IFile of this File and its Parent Folder
public IFile iFile { get; set; }
public IFolder IParent { get { return Parent.iFolder; } } // Parent IFolder
}
File CurrentFile = new File();
// Create the RootFolder in LocalStorage
async void CreateRootFolder()
{
CurrentFolder = RootFolder;
// PCL Storage: Create the IFolder of RootFolder (RootFolder.iFolder)
RootFolder.iFolder = await ICreateFolder(RootFolder, ILocalStorage);
LocalStorage.SubFolders.Add(RootFolder);
}
// Check existence of IFolder "name" in IFolder "iparent"
async Task<bool> ICheckFolderExist(string name, IFolder iparent)
{
ExistenceCheckResult IsExist = await iparent.CheckExistsAsync(name);
if (IsExist == ExistenceCheckResult.FolderExists) return true;
return false;
}
// Create a new IFolder of the Folder "folder" in the IFolder "iparent"
async Task<IFolder> ICreateFolder(Folder folder, IFolder iparent, bool replace = false)
{
if(replace) return await iparent.CreateFolderAsync(folder.Name, CreationCollisionOption.ReplaceExisting);
else return await iparent.CreateFolderAsync(folder.Name, CreationCollisionOption.GenerateUniqueName);
}
// Delete the IFolder "ifolder" in the IFolder "iparent"
async Task IDeleteFolder(IFolder ifolder, IFolder iparent)
{
// Check the existence of the IFolder corresponding to the Object "folder"
bool IsExist = await ICheckFolderExist(ifolder.Name, iparent);
if (!IsExist)
{
await DisplayAlert("", ifolder.Name.ToString() + " doesn't exist", "OK");
return;
}
await ifolder.DeleteAsync();
}
// Delete all of Folders and Files in the Folder "folder"
async void IDleteAllObjects(Folder folder)
{
// Get ALl IFolders and IFiles in the Folder "folder"
IList<IFolder> ifolders = await folder.iFolder.GetFoldersAsync();
IList<IFile> ifiles = await folder.iFolder.GetFilesAsync();
// Delete All IFolders
foreach (IFolder ifolder in ifolders)
{
await ifolder.DeleteAsync();
}
// Delete All IFiles
foreach (IFile ifile in ifiles)
{
await ifile.DeleteAsync();
}
// Delete All Folders & Files
folder.SubFolders.Clear();
folder.Files.Clear();
// Recreate the new RootFolder
CreateRootFolder();
}
// Get All IFolders in the IFolder "iparent"
async Task<IList<IFolder>> IGetFolders(IFolder iparent)
{
return await iparent.GetFoldersAsync();
}
// Check existence of IFile "name" in IFolder "iparent"
async Task<bool> ICheckFileExist(string name, IFolder iparent)
{
ExistenceCheckResult IsExist = await iparent.CheckExistsAsync(name);
if (IsExist == ExistenceCheckResult.FileExists) return true;
return false;
}
//Create a new IFile of the File "file" in the IFolder "iparent"
// (If the IFile already exists, open it.)
async Task<IFile> ICreateFile(File file, IFolder iparent)
{
return await iparent.CreateFileAsync(file.Name, CreationCollisionOption.ReplaceExisting);
}
// Delete IFile "ifile" in IFolder "iparent"
async Task IDeleteFile(IFile ifile, IFolder iparent)
{
// Check the existence of "file"
bool IsExist = await ICheckFileExist(ifile.Name, iparent);
if (!IsExist)
{
await DisplayAlert("", ifile.Name.ToString() + " doesn't exist", "OK");
return;
}
await ifile.DeleteAsync(); // Delete the IFile "file"
}
// Get All IFiles in in the IFolder "iparent"
async void IGetFiles(IFolder iparent)
{
IList<IFile> ifiles = await iparent.GetFilesAsync();
}
// Write "content" to IFile "ifile"
async void IWriteFile(IFile ifile, string content)
{
await ifile.WriteAllTextAsync(content);
}
// Read all text of IFile "ifile"
async Task<string> IReadFile(IFile ifile)
{
string content = await ifile.ReadAllTextAsync();
return content;
}
/*
// Open IFile corresponding to the Object "file",
// and set its content in the Editor "editor"
async void IOpenFile(Object file, Editor editor)
{
string content = await (file.iFile).ReadAllTextAsync();
editor.Text = content;
}
*/
// Get All IFolders and IFiles in IFolder "parent"
async void IGetAllObjects(IFolder parent)
{
await DisplayAlert("", "Get All IFolders and IFiles in parent", "OK");
}
// Copy, Cut, Paste IFolder
async void ICopyFolder()
{
await DisplayAlert("", "Copy IFolder", "OK");
}
async void ICutFolder()
{
await DisplayAlert("", "Cut IFolder", "OK");
}
async void IPasteFolder()
{
await DisplayAlert("", "Paste IFolder", "OK");
}
// Copy, Cut, Paste IFile
async void ICopyFile()
{
await DisplayAlert("", "Copy IFile", "OK");
}
async void ICutFile()
{
await DisplayAlert("", "Cut IFile", "OK");
}
async void IPasteFile()
{
await DisplayAlert("", "Paste IFile", "OK");
}
}
}
using System.Threading.Tasks;
using Xamarin.Forms;
using PCLStorage;
namespace CommandPrompt
{
public partial class MainPage : ContentPage
{
// Set LocalStorage
// Name: ILocalStorage.Name, Path: ILocalStorage.Path
static IFolder ILocalStorage = FileSystem.Current.LocalStorage;
// Create Folder class of LocalStorage and RootFolder
static Folder LocalStorage = new Folder()
{
Name = "LocalStorage",
Parent = null,
iFolder = ILocalStorage
};
static Folder RootFolder = new Folder()
{
Name = "RootFolder",
Parent = LocalStorage
};
Folder CurrentFolder = RootFolder;
// Folsder class
class Folder
{
public string Name { get; set; } // Folder Name
public Folder Parent { get; set; } // Parent Folder
private string path()
{
if (Name == "LocalStorage") return "LocalStorage>";
else if (Name == "RootFolder") return ">";
else if (Parent == RootFolder) return Name + ">";
else return Parent.Path + Name + ">";
}
public string Path { get { return path(); } } // Directory Path
// SubFolder and File List in this Folder
public List<Folder> SubFolders = new List<Folder>();
public List<File> Files = new List<File>();
public int NumChildren // Number of Children in this folder
{
get { return SubFolders.Count + Files.Count; }
}
public string NumChildrenText // Text of NoChildren
{
get { return "(" + NumChildren.ToString() + ")"; }
}
// IFolder of this Folder and its Parent Folder
public IFolder iFolder { get; set; }
public IFolder IParent { get { return Parent.iFolder; } } // Parent IFolder
}
// File class
class File
{
public string Name { get; set; } // File Name
public Folder Parent { get; set; } // Parent Folder
public string Path { get { return Parent.Path; } } // Directory Path
// IFile of this File and its Parent Folder
public IFile iFile { get; set; }
public IFolder IParent { get { return Parent.iFolder; } } // Parent IFolder
}
File CurrentFile = new File();
// Create the RootFolder in LocalStorage
async void CreateRootFolder()
{
CurrentFolder = RootFolder;
// PCL Storage: Create the IFolder of RootFolder (RootFolder.iFolder)
RootFolder.iFolder = await ICreateFolder(RootFolder, ILocalStorage);
LocalStorage.SubFolders.Add(RootFolder);
}
// Check existence of IFolder "name" in IFolder "iparent"
async Task<bool> ICheckFolderExist(string name, IFolder iparent)
{
ExistenceCheckResult IsExist = await iparent.CheckExistsAsync(name);
if (IsExist == ExistenceCheckResult.FolderExists) return true;
return false;
}
// Create a new IFolder of the Folder "folder" in the IFolder "iparent"
async Task<IFolder> ICreateFolder(Folder folder, IFolder iparent, bool replace = false)
{
if(replace) return await iparent.CreateFolderAsync(folder.Name, CreationCollisionOption.ReplaceExisting);
else return await iparent.CreateFolderAsync(folder.Name, CreationCollisionOption.GenerateUniqueName);
}
// Delete the IFolder "ifolder" in the IFolder "iparent"
async Task IDeleteFolder(IFolder ifolder, IFolder iparent)
{
// Check the existence of the IFolder corresponding to the Object "folder"
bool IsExist = await ICheckFolderExist(ifolder.Name, iparent);
if (!IsExist)
{
await DisplayAlert("", ifolder.Name.ToString() + " doesn't exist", "OK");
return;
}
await ifolder.DeleteAsync();
}
// Delete all of Folders and Files in the Folder "folder"
async void IDleteAllObjects(Folder folder)
{
// Get ALl IFolders and IFiles in the Folder "folder"
IList<IFolder> ifolders = await folder.iFolder.GetFoldersAsync();
IList<IFile> ifiles = await folder.iFolder.GetFilesAsync();
// Delete All IFolders
foreach (IFolder ifolder in ifolders)
{
await ifolder.DeleteAsync();
}
// Delete All IFiles
foreach (IFile ifile in ifiles)
{
await ifile.DeleteAsync();
}
// Delete All Folders & Files
folder.SubFolders.Clear();
folder.Files.Clear();
// Recreate the new RootFolder
CreateRootFolder();
}
// Get All IFolders in the IFolder "iparent"
async Task<IList<IFolder>> IGetFolders(IFolder iparent)
{
return await iparent.GetFoldersAsync();
}
// Check existence of IFile "name" in IFolder "iparent"
async Task<bool> ICheckFileExist(string name, IFolder iparent)
{
ExistenceCheckResult IsExist = await iparent.CheckExistsAsync(name);
if (IsExist == ExistenceCheckResult.FileExists) return true;
return false;
}
//Create a new IFile of the File "file" in the IFolder "iparent"
// (If the IFile already exists, open it.)
async Task<IFile> ICreateFile(File file, IFolder iparent)
{
return await iparent.CreateFileAsync(file.Name, CreationCollisionOption.ReplaceExisting);
}
// Delete IFile "ifile" in IFolder "iparent"
async Task IDeleteFile(IFile ifile, IFolder iparent)
{
// Check the existence of "file"
bool IsExist = await ICheckFileExist(ifile.Name, iparent);
if (!IsExist)
{
await DisplayAlert("", ifile.Name.ToString() + " doesn't exist", "OK");
return;
}
await ifile.DeleteAsync(); // Delete the IFile "file"
}
// Get All IFiles in in the IFolder "iparent"
async void IGetFiles(IFolder iparent)
{
IList<IFile> ifiles = await iparent.GetFilesAsync();
}
// Write "content" to IFile "ifile"
async void IWriteFile(IFile ifile, string content)
{
await ifile.WriteAllTextAsync(content);
}
// Read all text of IFile "ifile"
async Task<string> IReadFile(IFile ifile)
{
string content = await ifile.ReadAllTextAsync();
return content;
}
/*
// Open IFile corresponding to the Object "file",
// and set its content in the Editor "editor"
async void IOpenFile(Object file, Editor editor)
{
string content = await (file.iFile).ReadAllTextAsync();
editor.Text = content;
}
*/
// Get All IFolders and IFiles in IFolder "parent"
async void IGetAllObjects(IFolder parent)
{
await DisplayAlert("", "Get All IFolders and IFiles in parent", "OK");
}
// Copy, Cut, Paste IFolder
async void ICopyFolder()
{
await DisplayAlert("", "Copy IFolder", "OK");
}
async void ICutFolder()
{
await DisplayAlert("", "Cut IFolder", "OK");
}
async void IPasteFolder()
{
await DisplayAlert("", "Paste IFolder", "OK");
}
// Copy, Cut, Paste IFile
async void ICopyFile()
{
await DisplayAlert("", "Copy IFile", "OK");
}
async void ICutFile()
{
await DisplayAlert("", "Cut IFile", "OK");
}
async void IPasteFile()
{
await DisplayAlert("", "Paste IFile", "OK");
}
}
}
The methods for PCL Storage is not so different from the ones of PCLStorage (2). The arguments of some methods are changed. And the “Folder” and “File” class are newly defined, and they make it easier to refer some properties.
A movie showing this App running. You can get the original movie file from here (in "movie" folder). Since the PCL Storage is used in this App, folders and files are actually created in the LocalStorage of the PC in which the iOS simulator is running. So the movie also shows the LocalStorage folder.
Finally, I know that Command-Prompt-Like UI is not suitable for mobile application, but this is also my training!
コメント
コメントを投稿