title: Managing GUI Element Access Outside of Main Form Class in WinForms
body: <p>I am in the middle of refactoring a WinForms application that allows for interacting with EEPROM registers within a certain product over a USB to I2C adapter. The main form has a number of different controls on it (labels, textboxes, progress bar, etc) but is mostly centered around a DataGridView which displays the register addresses and their values that have either been read from the product directly or manually entered/changed by a user.</p>
  <p>I initially wrote the whole program in one monolithic Form class. As I learned more I realized that this is probably not the best way to organize the program. The main way I am trying to improve the code is by applying the single responsibility principle (SRP) and breaking the massive class up into a bunch of smaller ones. The primary issue that I am having with this approach is delegating the updates of Controls to classes that are separate from the MainForm class. I am trying to write the classes in a way that is loosely coupled to the specific implementation of the main Form, but this has proven difficult when I have to pass specific controls to classes in order to update them.</p>
  <p>For example below is a simplified version of a &quot;DataChangeNotifier&quot; class which has the concrete responsibility to update some labels' text and location as well as colors of various elements. I would like this class (or maybe something else) to handle the more abstract responsibility of &quot;updating UI elements which notify the user that a change in the data has occurred and has or has not been saved&quot;. You can see this class is very tightly coupled to the MainForm in that it is written to update specific labels and position them with respect to other specific controls. The solution that I keep seeing to this problem would amount to just returning a string for the label text and handling the actual updates to the controls from within the MainForm class. That way the DataChangeNotifier class wouldn't depend on a WinForms label and could be used with some other GUI framework. However, I have a lot of these classes that interact with controls and if I put the code to actually update all of these controls in the MainForm class then I think I would end up with a pretty large class, which is exactly what I am trying to get away from.</p>
  <p>Maybe I'm missing something fundamental here, but I can't figure out how to balance loose coupling with SRP. Perhaps I'm trying to do something conflicting here... make a class update specific GUI elements while not being dependent on those specific GUI elements existing...? Maybe if I want to break up the handling of Controls to separate classes I need a concrete class for specific implementation of the GUI and an interface or abstract class, which I can manipulate in the business logic part of the program. Is that the right way to think of the problem?</p>
  <p>If it is how should I go about creating an abstract class or interface for something like the DataChangeNotifier class?</p>
  <p>If not then how should I organize my program so classes like DataChangeNotifier can still take on the responsibility of updating GUI elements, but also not depend on the specific implementation of those GUI elements?</p>
  <pre class="lang-cs prettyprint-override"><code>public class MainForm : Form
  {
      // Example of some of the controls in the MainForm
      public DataGridView MainDataGridView;
      public Label ChangeNotifierLabel;
      public Label ReadWriteStatusLabel;
      public ProgressBar ReadWriteProgressBar;
      
      // I have to pass specific GUI elements to the constructor of DataChangeNotifier
      public DataChangeNotifier MainChangeNotifier = new DataChangeNotifier(
          MainDataGridView,
          ChangeNotifierLabel,
          ReadWriteStatusLabel,
          ReadWriteProgressBar);
  }
  
  public class DataChangeNotifier
  {
      private DataGridView _dataGridView;
      private Label _changeNotifierLabel;
      private Label _readWriteStatusLabel;
      public ProgressBar _readWriteProgressBar;
      
      // Set label text, update label location, set colors of some elements...
      public void SetChangeNotifierSaved()
      {
          ChangeNotifierLabel.Text = &quot;Saved&quot;;
          ChangeNotifierLabel.Left = _dataGridView.Location.X + _dataGridView.Width - ChangeNotifierLabel.Width;
          SetNotifierColorsSaved();
      }
  
      //Set label text and update label location.
      public void UpdateReadWriteStatusLabel(string statusMessage)
      {
          _readWriteStatusLabel.Text = statusMessage;
          _ReadWriteStatusLabel.Left = _readWriteProgressBar.Left + (_readWriteProgressBar.Width - _readWriteStatusLabel.Width);
      }
  
      private void SetNotifierColorsSaved(){...}
  
      // Constructor requires specific controls, making this class dependent on the GUI having these
      // specific controls.
      public DataChangeNotifier(DataGridView dataGridView, Label changeNotifierLabel, Label readWriteStatusLabel, ProgressBar readWriteProgressBar;)
      {
          _dataGridView = dataGridView;
          _changeNotifierLabel = changeNotifierLabel;
          _readWriteStatusLabel = readWriteStatusLabel;
          _readWriteProgressBar = readWriteProgressBar;
      }
  }
  
  </code></pre>