eBookReader
This is a e-Book Reader for text files that uses a file splitter

Introduction and Background
This is an attempt at creating an e-Book Reader. Right now, it only reads txt files. I will be adding *.doc and *.rtf files for a more visual effect. It uses a File Splitter
that can split files to a specified range (2000 to 500000). The File Splitter
can be found here at CP.
I re-edited the DLL file to produce nothing but TXT files, as this makes it a lot easier and quicker to read after splitting a much larger file into smaller pieces. It also adds the correct name and part numbers so when displayed in the listbox
, they show up in alphabetical order. (See picture above.)
The eBookReader
will also keep track of your last read place. When you are at the end of the current file part, it automatically advances to the next file part and displays it in the RichTextBox
. If you want to take a break from reading, you can save your position by clicking the menu item Save Position
and when you want to continue, just open the same book and click the menu item Get Position
. It will display where you left off at the top of the book part that you were last reading.
Using the Code
YOU MUST RUN AS ADMINISTRATOR. This is a must as we are creating and reading keys from the Registry
and creating Folders
to store our e-books
in after splitting them.
When the application is first booted, it will create a folder named "eBooks
" in your Documents folder. If it already exists, then we exit the sub
gracefully and continue on with the Form loading
. This is the reason for "Running As Administrator" and later when we write and read values to and from the Registry
.
If you do not have any e-books to read, I am posting a few sites in the Points Of Interest section at the bottom of this page. Here is a FREE e-book site that I use, wwwgutenbergorg, in the menu item E-Books
. When selecting your books to read, make sure to press the txt
format.
You will have to copy and paste, so make sure you have a text editor open (Notepad or Notepad ++), then save it as Name of the file.txt
. This is a little time consuming, but, the books are FREE.
Now that we have 4 or 5 ebooks to read, let's start splitting them by clicking the File Splitter
menu item
, then Split File
, this form appears...

For the source file, enter one of the files you just saved. For a destination, select the folder eBooks
that we created when the form first loaded. Click the split button (btnGo
).
Another folder gets created and named after the file that we are splitting (FileName.txt). The files that get created are (FileName000.txt, FileName001.txt, FileName002.txt) and so on. When the numbers are added on, this is one of the areas in the DLL file that I re-edited, they are added so that they show up in alphabetical order. DLL code here...
do
{
if (nCounter <= 9)
m_fsOut = new FileStream(m_OutDir + "\\" +
Path.GetFileNameWithoutExtension(m_FileName)
+ "00" + nCounter.ToString() + ".txt", FileMode.Create);
else if (nCounter <= 99)
m_fsOut = new FileStream(m_OutDir + "\\" +
Path.GetFileNameWithoutExtension(m_FileName)
+ "0" + nCounter.ToString() + ".txt", FileMode.Create);
else
{
m_fsOut = new FileStream(m_OutDir + "\\" +
Path.GetFileNameWithoutExtension(m_FileName)
+ nCounter.ToString() + ".txt", FileMode.Create);
}
//code continues
As I do not know how to program in C#
yet, this seemed to work very well. The main picture up above shows the results of this code in the ListBox
. I also changed all the ".part"
extensions to ".txt" in both the Splitter Class
and the Merger Class
that reside in the The_FileSplitter_v2 DLL.
In the btnGo_Click
event, we call the fs.BeginSplit()
sub
(below). This, in turn, calls the code that follows (Split() sub)
. 80% of the splitting is done here. The other 20% is in the sub
s, and or functions that we do not call.
public void BeginSplit()
{
#if DEBUG
split();
#else
m_tdSplitter = new Thread(new ThreadStart(split));
m_tdSplitter.Priority = ThreadPriority.AboveNormal;
m_tdSplitter.IsBackground = false;
m_tdSplitter.Name = "Splitting";
m_TimeStart = DateTime.Now;
m_tdSplitter.Start();
#endif
}
In order to program in the .NET Framework, you have to be multi-lingual, using any of the .NET languages that are out there. Although I can't program in C# yet, I can read it.
In the following Split sub below, is where the 80% takes place...
- We set the
m_CacheSize
equal to them_SizeLimit
, if larger - We declare a buffer and set it to the
m_CacheSize
- We set
m_fsin
equal to aNew FileStream
- We set
m_bReader
equal to aNew BinaryReader
- We create a
Directory
if one does not exist - We name each file so they display in order.
- We set
cBuffer
equal to aNew byte[m_fsIn.Length - m_fsIn.Position]
- After doing a few more steps, we get the position and start over again.
private void split()
{
if (m_CacheSize > m_SizeLimit)
m_CacheSize = (uint)m_SizeLimit;
byte[] cBuffer = new byte[m_CacheSize];
int nCounter = 0;
m_fsIn = new FileStream(m_FileName, FileMode.Open, FileAccess.Read);
m_bReader = new BinaryReader(m_fsIn);
if (!Directory.Exists(m_OutDir))
Directory.CreateDirectory(m_OutDir);
else
{
Directory.Delete(m_OutDir, true);
Directory.CreateDirectory(m_OutDir);
}
int reads = 0;
try
{
do
{
if (nCounter <= 9)
m_fsOut = new FileStream(m_OutDir + "\\" +
Path.GetFileNameWithoutExtension(m_FileName)
+ "00" + nCounter.ToString() + ".txt", FileMode.Create);
else if (nCounter <= 99)
m_fsOut = new FileStream(m_OutDir + "\\" +
Path.GetFileNameWithoutExtension(m_FileName)
+ "0" + nCounter.ToString() + ".txt", FileMode.Create);
else
{
m_fsOut = new FileStream(m_OutDir + "\\" +
Path.GetFileNameWithoutExtension(m_FileName)
+ nCounter.ToString() + ".txt", FileMode.Create);
}
do
{
if ((m_fsIn.Length - m_fsIn.Position) < cBuffer.Length)
cBuffer = new byte[m_fsIn.Length - m_fsIn.Position];
reads = m_bReader.Read(cBuffer, 0, cBuffer.Length);
m_bWriter = new BinaryWriter(m_fsOut);
m_bWriter.Write(cBuffer, 0, reads);
m_Written += reads; // = fsIn.Position;
m_Progress = (uint)((float)m_Written * 100 / (float)m_FileSize);
OnPartialSplitDone(EventArgs.Empty);
} while ((m_fsOut.Position < m_SizeLimit) &&
(m_fsIn.Position < m_FileSize));
m_bWriter.BaseStream.Close();
m_Written = m_fsIn.Position;
nCounter++;
m_Progress = (uint)((float)m_Written * 100 / (float)m_FileSize);
OnPartialSplitDone(EventArgs.Empty);
} while ((m_fsIn.Position < m_fsIn.Length));
m_bReader.BaseStream.Close();
OnSplitDone(EventArgs.Empty);
}
catch (Exception e)
{
m_SplitErrorMessage = e.Message;
OnError(EventArgs.Empty);
abort();
}
GC.Collect();
}
Now that the splitting part is out of the way, we can concentrate on the eBookReader
.
I originally started this project for my grand-daughter, But, I decided to post it. She still uses it.
There are 17 subs and functions, but we will be going over a select few as most of them are pretty straight forward. I have commented just about every line of code except for code that explains itself (example: Application.Exit()
), etc...). The reading from and writing to the registry
, advancing automatically to the next book part, and setting/finding the last place you read from, are pieces we will examine.
-
Private Sub OpentextFile()
This sub
is fairly standard for a file read. All it does is it opens a file, reads a line, writes it to a RichTextBox
and we do it again and again until our file is at its end. We set the NumberOfPages
, close the file, and display the results to the user. Code follows...
Private Sub OpentextFile(ByVal fname As String)
'Clear the RichTextBox and
'set the line count equal to zero.
rtb1.Clear()
lineCount = 0
'Declare a FileStream and a StreamReader.
Dim fs As FileStream
Dim sr As StreamReader
'Declare a place holder for each string we read.
Dim strFile As String
Try
fs = New FileStream(fname, FileMode.Open, FileAccess.Read)
sr = New StreamReader(fs)
strFile = sr.ReadLine()
'While the file is opened,
'we read each line and put
'it in the RichTextBox and
'add 1 to our line count.
'We do it repeatedly until
'we cant do it no more.
Do Until strFile Is Nothing
rtb1.Text &= strFile & vbCrLf
lineCount += 1
strFile = sr.ReadLine()
Loop
'We set the NumberOfPages equal to
'the lineCount divided by the
'NUMBEROFLINES constant.
NumberOfPages = lineCount / NUMBEROFLINES
'Close the file.
fs.Close()
'Display the to the user, the
'number of pages, and the line count.
lblStatus.Text = "Number of Pages = " & NumberOfPages.ToString()
lblTotalLineCount.Text = "Total Line Count = " & lineCount.ToString()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
-
Private Sub mnuFSavePosition_Click()
We have to save a spot in our book part so we can continue reading when we return to start reading again. We do this by setting the currentFolder string
when we click the menu Open
button. This opens a FolderDialog
box and we then select a folder to open and retrieve the files in it.
Next, we have to set a position to return to. I was going to set the caret
position, but I could not find away to do this. The only way I found was in WPF
, and I was not going to change the application all around just for a caret setting. So I settled for some simple highlighting
. This is very simple, just highlight some text at the beginning of the next sentence where you want to begin reading, then click the menu item Save Position
. Code follows...
Private Sub mnuFSavePosition_Click(sender As System.Object,
e As System.EventArgs) _
Handles mnuFSavePosition.Click
'If the current folder is equal to an empty string
'Exit the sub and put something in it.
If currentFolder = "" Then Exit Sub
'Select some text so we can start reading where we left off.
mySelectedText = rtb1.SelectedText
'set the current book part
selection = lstFiles.SelectedItem
'Set are Registry values
My.Computer.Registry.SetValue("HKEY_LOCAL_MACHINE\SOFTWARE\eBookReader",
"selection", selection)
My.Computer.Registry.SetValue("HKEY_LOCAL_MACHINE\SOFTWARE\eBookReader",
"folder", selectedFolder)
My.Computer.Registry.SetValue("HKEY_LOCAL_MACHINE\SOFTWARE\eBookReader",
"mySelectedText", mySelectedText)
'Set the Booleans
CanBeSaved = False
saved = True
End Sub
-
Private Sub mnuFGetPosition_Click()
Now that we have our settings set in the Registry
and our position saved, we can now retrieve it and place the settings back into the original variables. If no text was previously selected, then we display a message stating this fact, otherwise we can continue. Our selected text from a previous read will be highlighted and set at the top of our RichTextBox(HighLighted)
.
Private Sub mnuFGetPosition_Click(sender As System.Object,
e As System.EventArgs) Handles mnuFGetPosition.Click
If My.Computer.Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\eBookReader",
"selection",
selection) <> "" Then
If currentFolder = My.Computer.Registry.GetValue _
("HKEY_LOCAL_MACHINE\SOFTWARE\eBookReader",
"folder", selectedFolder) Then
'Get the last read Book part
lstFiles.SelectedItem = My.Computer.Registry.GetValue _
("HKEY_LOCAL_MACHINE\SOFTWARE\eBookReader",
"selection", selection)
'Get the last selected text so we can start reading
'where we left off.
mySelectedText = My.Computer.Registry.GetValue _
("HKEY_LOCAL_MACHINE\SOFTWARE\eBookReader",
"mySelectedText", mySelectedText)
'If RichTextBox has no selected text...Then?
If mySelectedText = "" Then
MessageBox.Show("Selected text was not set during the last read...", _
"Highlight Info",
MessageBoxButtons.OK, MessageBoxIcon.Information)
Exit Sub
End If
'Display the selected text at the top of
'the RichTextBox
FindMyText(mySelectedText, 0, rtb1.Text.Length)
'Set the caret position
rtb1.ScrollToCaret()
'Set the Booleans
CanBeSaved = True
saved = False
Else
mySelectedText = ""
CanBeSaved = False
saved = True
Exit Sub
End If
Else
mySelectedText = ""
End If
End Sub
-
Public Function FindMyText()
In the sub displayed above, you will see this line, "FindMyText(mySelectedText, 0, rtb1.Text.Length)"
. This is called so we can use the RichTextBox.Find
method. When FindMyText
is called, it should look like this... FindMyText(mySelectedText, 0, rtb1.Text.Length)
. It uses the selected text that we called from the Registry, a starting point (beginning of the file), and an ending point (end of file). This function returns the caret position (indexToText
), but in the form of highlighted text.
Public Function FindMyText(ByVal searchText As String,
ByVal searchStart As Integer,
ByVal searchEnd As Integer) As Integer
' Initialize the return value to false by default.
Dim returnValue As Integer = -1
' Ensure that a search string and a valid starting point are specified.
If searchText.Length > 0 And searchStart >= 0 Then
' Ensure that a valid ending value is provided.
If searchEnd > searchStart Or searchEnd = -1 Then
' Obtain the location of the search string in rtb1.
Dim indexToText As Integer = rtb1.Find(searchText, searchStart,
searchEnd,
RichTextBoxFinds.MatchCase)
' Determine whether the text was found in rtb1.
If indexToText >= 0 Then
' Return the index to the specified search text.
returnValue = indexToText
End If
End If
End If
Return returnValue
End Function
-
Private Sub nextBookPartTimer_Tick()
As we are reading along at a steady pace, we get to the end of the file. Now we have to find the next book part. Forget this. This sub
does this for you by auto-matically going to the next book part. It clears the just read book part from the RichTextBox
and opens the next book part by calling this sub
... lstFiles_SelectedIndexChanged()
. This sub
actually gets called when we advance the selected index. Code below...
Private Sub nextBookPartTimer_Tick(sender As Object,
e As System.EventArgs) _
Handles nextBookPartTimer.Tick
If lineNumber = lineCount Then
'Clear the RichTextBox
rtb1.Clear()
'Advance to the next book part automatically
lstFiles.SelectedIndex += 1
End If
End Sub
-
Private Sub rtb1_KeyDown()
All this sub
does is to keep track of our current caret position by displaying this to the user in labels that are in the StatusStrip
at the bottom of the form.
Private Sub rtb1_KeyDown(sender As Object,
e As System.Windows.Forms.KeyEventArgs) Handles rtb1.KeyDown
'Keep track of all positions when pressing the arrow keys.
If e.KeyData = Keys.Down Then
lineNumber = rtb1.GetLineFromCharIndex(rtb1.SelectionStart) + 1
intPosition = rtb1.SelectionStart - rtb1.GetFirstCharIndexOfCurrentLine() + 1
lblLineNumber.Text = "Line Number = " & lineNumber.ToString
lblColumn.Text = "Column Position = " & intPosition.ToString()
rtb1.Focus()
ElseIf e.KeyData = Keys.Up Then
lineNumber = rtb1.GetLineFromCharIndex(rtb1.SelectionStart) + 1
intPosition = rtb1.SelectionStart - rtb1.GetFirstCharIndexOfCurrentLine() + 1
lblLineNumber.Text = "Line Number = " & lineNumber.ToString
lblColumn.Text = "Column Position = " & intPosition.ToString()
rtb1.Focus()
ElseIf e.KeyData = Keys.Left Then
lineNumber = rtb1.GetLineFromCharIndex(rtb1.SelectionStart) + 1
intPosition = rtb1.SelectionStart - rtb1.GetFirstCharIndexOfCurrentLine() + 1
lblLineNumber.Text = "Line Number = " & lineNumber.ToString
lblColumn.Text = "Column Position = " & intPosition.ToString()
rtb1.Focus()
ElseIf e.KeyData = Keys.Right Then
lineNumber = rtb1.GetLineFromCharIndex(rtb1.SelectionStart) + 1
intPosition = rtb1.SelectionStart - rtb1.GetFirstCharIndexOfCurrentLine() + 1
lblLineNumber.Text = "Line Number = " & lineNumber.ToString
lblColumn.Text = "Column Position = " & intPosition.ToString()
rtb1.Focus()
End If
End Sub
Points of Interest
- The Gutenberg Project
- Free Book Spot
- Free Ebooks
- Many Books
- Get Free E-books
- Free Computer Books
- Notepad ++
History
- Original post 10·08·2011