1

I created the editor inheriting the TextBox.

The editor loads the data from the file like below.

public Editor()
{
    this.Text = File.ReadAllText(fileName);
}

The above code works well but the changes of the editor do not apply because not be bound with the file.

In other words, although I change the content of the editor then the changes do not apply to the file.

To do this, I need to write additional code as below.

File.WriteAllText(fileName, content);

The problem is I don't know where to put the above code.

If I put the above code onto the TextChanged event handler then the performance of the editor is lower.

But I don't know the suitable position to put the above code except for TextChanged event handler.

Is there a formulaic way to solve this problem?

nalka
  • 1,894
  • 11
  • 26
jjw
  • 282
  • 3
  • 20
  • 4
    The alternative to constantly saving in a TextChanged handler is of course the usual Save button. – Clemens Jan 24 '20 at 10:50
  • Just like any other text editor, you need to give the user a File, Save menu command to actually save the file. – Polyfun Jan 24 '20 at 10:53
  • If in the `TextChanged` event you copied the contents into a separate string and then *in a separate thread* you wrote to the file system you would likely have reduced impact on the user experience. _But the Save button is probably a better idea._ – mjwills Jan 24 '20 at 10:55
  • 1
    Typical editor have File/Save menu and hotkey. To prevent user loosing data you can also save regularly (timer) backup copy and in case of crash check on startup if such backup exists and restore data from it. – Sinatr Jan 24 '20 at 11:01
  • Thanks for comments. In conclusion, the formulaic way not exist so I have to implement it myself. – jjw Jan 24 '20 at 14:09

3 Answers3

3

Is there a formulaic way to solve this problem?

No, there isn't. You as the control developer needs to decide when to write the data back to the file.

The common solution to this kind of issues is to implemement some sort of throttling behavior, i.e. when you detect a key stroke you wait x seconds before you write the data back to the storage.

Please have a look at how ReactiveUI solves this using Rx and LINQ operators. You might also use a timer that you for example start when a TextChanged event is raised.

The other option is of course to provide a button or some other input control that lets the user decide when to save.

mm8
  • 163,881
  • 10
  • 57
  • 88
2

First of all, use timer to save text with some period (approx 5 sec). Save text async to avoid performance issue.

    private bool needSave;

    private object locker = new object();

    private void Form1_Load(object sender, EventArgs e)
    {
        textBox1.Text = File.ReadAllText("test.txt");

        timer1.Interval = 5000;

        timer1.Enabled = true;
    }

    private async void timer1_Tick(object sender, EventArgs e)
    {
        if (needSave)
        {
            needSave = false;

            await Task.Run(() => Save());
        }
    }

    private void Save()
    {
        lock (locker)
        {
            File.WriteAllText("test.txt", textBox1.Text);
        }
    }

    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        needSave = true;
    }
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • 1
    Not sure if `textBox1.Text` can be accessed from a task's thread. And instead of `Task.Run(() => Save());` better use async I/O in the first place. – Clemens Jan 24 '20 at 12:11
1

A similar way to @Sviatoslav Vovdenko's solution is this

private void Form1_Load(object sender, EventArgs e)
{
    textBox1.Text = File.ReadAllText("test.txt");

    timer1.Interval = 2000;
    timer1.Enabled = false;  //***********
}

private async void timer1_Tick(object sender, EventArgs e)
{ 
    timer1.Stop();   //***********
    await Task.Run(() => Save()); 
}

private void Save()
{
    lock (this.timer1)     //***********
        File.WriteAllText("test.txt", textBox1.Text); 
}

private void textBox1_TextChanged(object sender, EventArgs e)
{
    timer1.Stop();     //*********** skip pending save and start again
    timer1.Start();    //***********
}
mrbm
  • 1,136
  • 1
  • 12
  • 36
  • 1
    Please see [Why is lock(this) {…} bad?](https://stackoverflow.com/q/251391/1136211) – Clemens Jan 24 '20 at 12:22
  • @Clemens , thanks, what about 'lock(this.timer1)' ? – mrbm Jan 24 '20 at 12:28
  • Any private member will of course do. However, the lock is pointless. Better only restart the timer when no async I/O is currently pending. – Clemens Jan 24 '20 at 12:34