I'm developing a single window, digital signal processing application using WinForms, centered around tabs from a TabControl. The user can switch between tabs and do stuff. Each tab represents a stage of the data analysis (acquiring a signal -> clustering -> runnning a filter and looking at some graphs -> looking at some more graphs and data). The tabs are called: Acquisition, Clustering, Filtering and Identification.
I have defined a Signal class. You can run analysis (on the same window) for multiple signals (loaded or acquired) - naturally, I have a RuntimeData class holding a List<Signal> LoadedSignals and a List<Signal> AcquiredSignals. You must switch to the ListViewItem representing the Signal you want to analyze, and the UI updates accordingly.
I did some research on the Model-View-Presenter structure and how it fits well to WinForms, but I'm having a hard time understanding how to apply it in my case: if my Views are Tabs, how would they implement the IRespectiveTabView interface? And how would that work when I have multiple data subjects?
For example, I begin refactoring the old FilterRegion.cs class:
public class FilterRegion
{
public double PhiMin { get; set; }
public double PhiMax { get; set; }
public double VMin { get; set; }
public double VMax { get; set; }
// Interactive lines
public VLine LinePhiMin { get; set; } = new();
public VLine LinePhiMax { get; set; } = new();
public HLine LineVMin { get; set; } = new();
public HLine LineVMax { get; set; } = new();
public bool InvertedPhiLines()
{
return PhiMin > PhiMax;
}
public bool InvertedVLines()
{
return VMin > VMax;
}
public Color FilterPolyColor { get; set; }
public Polygon MainPoly { get; set; }
public Polygon LeftPoly { get; set; }
public Polygon RightPoly { get; set; }
double[] XsMainPoly;
double[] YsMainPoly;
double[] XsLeftPoly;
double[] XsRightPoly;
double[] nullVals = { 0, 0, 0, 0 };
public void EnableLinesInteraction()
{
LinePhiMin.DragEnabled = true;
LinePhiMax.DragEnabled = true;
LineVMin.DragEnabled = true;
LineVMax.DragEnabled = true;
}
public void DisableLinesInteraction()
{
LinePhiMin.DragEnabled = false;
LinePhiMax.DragEnabled = false;
LineVMin.DragEnabled = false;
LineVMax.DragEnabled = false;
}
public bool IsEmpty()
{
return PhiMin == 0 && PhiMax == 0 && VMin == 0 && VMax == 0;
}
public bool IsBroken()
{
return PhiMin > PhiMax;
}
public void Plot(FormsPlot targetPlot)
{
// Plotting logic.
}
public void UpdatePolys(FormsPlot targetPlot)
{
// Logic for the update of the filter polygons.
}
public void SaveManualFilter()
{
PhiMin = LinePhiMin.X;
PhiMax = LinePhiMax.X;
VMin = LineVMin.Y;
VMax = LineVMax.Y;
}
public void SetLineEvents(FormsPlot targetPlot)
{
LinePhiMin.Dragged += (s, e) =>
{
UpdatePolys(targetPlot);
SaveManualFilter();
};
LinePhiMax.Dragged += (s, e) =>
{
UpdatePolys(targetPlot);
SaveManualFilter();
};
LineVMin.Dragged += (s, e) =>
{
UpdatePolys(targetPlot);
SaveManualFilter();
};
LineVMax.Dragged += (s, e) =>
{
UpdatePolys(targetPlot);
SaveManualFilter();
};
}
}
The model class:
namespace App.Models
{
public class FilterRegionModel
{
public double PhiMin { get; set; }
public double PhiMax { get; set; }
public double VMin { get; set; }
public double VMax { get; set; }
public bool InvertedPhiLines()
{
return PhiMin > PhiMax;
}
public bool InvertedVLines()
{
return VMin > VMax;
}
public bool IsEmpty()
{
return PhiMin == 0 && PhiMax == 0 && VMin == 0 && VMax == 0;
}
public bool IsBroken()
{
return PhiMin > PhiMax;
}
}
}
The view interface:
namespace App.Views
{
public interface IFilterRegionView
{
public VLine LinePhiMin { get; set; }
public VLine LinePhiMax { get; set; }
public HLine LineVMin { get; set; }
public HLine LineVMax { get; set; }
public Color FilterPolyColor { get; set; }
public Polygon MainPoly { get; set; }
public Polygon LeftPoly { get; set; }
public Polygon RightPoly { get; set; }
public double[] XsMainPoly { get; set; }
public double[] YsMainPoly { get; set; }
public double[] XsLeftPoly { get; set; }
public double[] XsRightPoly { get; set; }
}
}
The presenter class:
namespace App.Presenters
{
public class FilterRegionPresenter
{
IFilterRegionView View;
public FilterRegionModel Model { get; set; }
public FilterRegionPresenter(IFilterRegionView view, FilterRegionModel model)
{
this.View = view;
Model = model;
}
public void Plot(FormsPlot targetPlot)
{
// Plotting logic.
}
public void Update(FormsPlot targetPlot)
{
// Polygon updating logic.
}
public void SetLineEvents(FormsPlot targetPlot)
{
// Adding functions that will run when events are raised.
}
}
}
But now I'm stuck on the problem that is applying it to the Form, given that I have TabPages. Each Signal has multiple Cluster objects the user can select, and each Cluster object will print a graph to a single plot (the same plot refreshes when selecting another Cluster). This graph will contain a pair of FilterRegion objects.