This is a good follow-up question to your SO post about saving text box between launches. I'm upvoting it, because it touches so many key points (and it's ok if you don't learn all of them at one time). TextBox often contains such a small amount of data that it's easy to save almost anywhere. We could improve the above code by saving only when the [Enter] key is pressed OR when the TextBox loses focus.
RichTextBox control is different. The document it displays might be quite large. For example, images (which are already big) are stored as text streams (which are even bigger). Generally it's not practical to use Properties.Settings to store it. As far as "when to save" we can't rely on the Enter key because that just inserts a newline character into the multiline control.
Good news: For all the controls you asked about, the basic flow is the same. When we know "what we want to do" but not "how we're going to do it" we can make a To-Do list that has a keyword of interface.
interface IPersistCommon // Things we need our custom control to do.
{
SaveType SaveType { get; set; } // Possible ways to save
void Save(); // Save in the manner selected by SaveType
void Load(); // Load in the manner selected by SaveType
}
The SaveType is an enumeration we made up ourselves. It describes possible ways to store data for different controls we design. We have to think about capacity, speed, and portability across platforms like WinOS, Android, and iOS. Here are a few possibilities:
enum SaveType
{
AppProperties, // Like the textbox code shown above
WindowsRegisty, // A traditional method, but Windows Only
File, // For example, an RTF file in Local AppData (cross-platform)
FileDataStore, // Mobile cross-platform
FileDataStoreJSON, // Serialize the object's content AND SETTINGS ('enabled' etc.)
SQLite // Mobile platforms also available
}
The rest is almost too easy! Take a control that has most of the functionality we want (in this case RichTextBox) and use inheritance to make a custom class that adds the additional functionality that we declared in our interface. This literally forces us to implement everything our To-Do list demands (otherwise it won't even build).
class PersistRichTextBox // Our own class...
: RichTextBox // ...that inherits the regular one
, IPersistCommon // ... and MUST implement SaveType, Save() and Load()
{ }
Implement SaveType with Browsable attribute.
[Browsable(true)]
public SaveType SaveType { get; set; }
...this way it's visible in Design Mode:

For RichTextBox, set it to SaveType.File which says to store RTF data in the AppData folder in a file with *.rtf extension when we implement the Save method:
public void Save()
{
switch (SaveType)
{
case SaveType.AppProperties:
// This would be a concern if the RTF for example
// holds an image file making it gigantic.
Properties.Settings.Default[Name] = Rtf;
Properties.Settings.Default.Save();
break;
case SaveType.File:
File.WriteAllText(FileName, Rtf);
break;
case SaveType.FileDataStore:
case SaveType.FileDataStoreJSON:
case SaveType.WindowsRegisty:
case SaveType.SQLite:
default:
throw new NotImplementedException("To do!");
}
Debug.WriteLine("Saved");
}
For RichTextBox, do Load from the same file:
public void Load()
{
if (!DesignMode)
{
BeginInit();
switch (SaveType)
{
case SaveType.AppProperties:
Rtf = (string)Properties.Settings.Default[Name];
break;
case SaveType.File:
if(File.Exists(FileName))
{
Rtf = File.ReadAllText(FileName);
}
break;
case SaveType.FileDataStore:
case SaveType.FileDataStoreJSON:
case SaveType.WindowsRegisty:
case SaveType.SQLite:
default:
throw new NotImplementedException("To do!");
}
EndInit();
}
}
Finally, as far as "when" to save, if user pastes an image or presses a key, wait for about a second to see of they're still typing. If there is inactivity after the interval time-out, do an auto-save.
protected override void OnTextChanged(EventArgs e)
{
// This will pick up Paste operations, too.
base.OnTextChanged(e);
if(!_initializing)
{
// Restarts a short inactivity WDT and autosaves when done.
WDT.Stop();
WDT.Start();
}
}
// Timeout has expired since the last change to the document.
private void WDT_Tick(object sender, EventArgs e)
{
WDT.Stop();
Save();
}
... where...
public PersistRichTextBox()
{
WDT = new Timer();
WDT.Interval = 1000;
WDT.Tick += WDT_Tick;
}
Timer WDT;
You can clone the full, working sample from our GitHub repo.