1
\$\begingroup\$

in my application i have a form 'DetailScreenView' which implements 'IDetailScreenView'. It is connected to 'DetailScreenPresenter' by the Passive View MVP pattern. I also have a 'DetailScreenUserControl' which needs to be put onto the 'DetailScreenView'.

I have looked at how other people have implemented it, such as in this post. https://stackoverflow.com/questions/7606955/how-implement-usercontrol-in-winforms-mvp-pattern But since my case is different, it did not really fit.

Please find my code with explanations below. I have a feeling this is not the correct way to do it, because the performance is really bad. When I check some boxes I can visibly see the adding of the user control for approximately 1 to 2 sec. depending on how many boxes are checked. With one box checked it is almost instantly.

If there is no better solution to handle this I will have to create a new form after checking the boxes so that I do not have to add anything dynamically.

EDIT: For now I am using this to remove the slow painting of the controls and flickering: https://stackoverflow.com/questions/2612487/how-to-fix-the-flickering-in-user-controls

DetailScreenView.cs I have a checked listbox on this form called 'questionStandardInput'. When the user checks a box, the '_MouseUp' event is raised. I raise two events here, one for the Presenter and one for the UserControl.

public partial class DetailScreenView : Form, IDetailScreenView
{
    private DetailScreenPresenter _presenter;
    private DetailScreenUserControl _userControl;

    public List<string> QuestionStandards
    {
        get { return questionStandardInput.Items.Cast<string>().ToList(); }
        set { questionStandardInput.DataSource = value; }
    }

    public List<string> SelectedQuestionStandards
    {
        get { return questionStandardInput.CheckedItems.Cast<string>().ToList(); }
    }

    public List<string> MaturityStandards
    {
        set { _userControl.InitializeMaturityInput(value); }
    }

    public List<string> ComplianceStandards
    {
        set { _userControl.InitializeComplianceInput(value); }
    }

    public Form DetailScreen
    {
        set { this.Controls.Add(value); }
    }

    public event EventHandler<EventArgs> InitializingStandards;
    public event EventHandler<EventArgs> InitializingUserControl;
    public event EventHandler<EventArgs> InitializingUserControlData;

    // Initialize detailscreen. Create an instance of the presenter with a reference the view itsself.
    public DetailScreenView()
    {
        InitializeComponent();
        _presenter = new DetailScreenPresenter(this);
        _userControl = new DetailScreenUserControl(this);
    }

    // Fires the initializing standards event.
    public void DetailScreenView_Activated(object sender, EventArgs e)
    {
        InitializingStandards?.Invoke(this, EventArgs.Empty);
    }

    // Fires the initializing user control and initializing user control data events.
    private void questionStandardInput_MouseUp(object sender, MouseEventArgs e)
    {
        InitializingUserControl?.Invoke(this, EventArgs.Empty);
        InitializingUserControlData?.Invoke(this, EventArgs.Empty);
    }

    // Adds the filled user control to the screen.
    public void AddUserControl()
    {
        this.Controls.Add(_userControl);
    }

    public void Message(string message)
    {
        MessageBox.Show(message);
    }

}       

DetailScreenPresenter This class is subscribed to the 'InitializingUserControlData' event from the View with two functions. Both these functions get data from the database through 'AssessmentsModel' class. The data is then set to the View's Lists 'MaturityStandards' and 'ComplianceStandards'.

class DetailScreenPresenter
{
    private readonly IDetailScreenView _view;
    private AssessmentsModel _assessmentsModel;

    // Initialize the detail screen presenter with an IDetailScreenView and a new assessments model. Subsribe to necessary events.
    public DetailScreenPresenter(IDetailScreenView view)
    {
        _view = view;
        _assessmentsModel = new AssessmentsModel();
        _view.InitializingStandards += InitializeStandards;
        _view.InitializingUserControlData += InitializeMaturityStandards;
        _view.InitializingUserControlData += InitializeComplianceStandards;
    }

    // Fills the checked list box of the view with all possible question standards. If none are found, message is sent to the view.
    public void InitializeStandards(object sender, EventArgs e)
    {
        List <string> questionStandards = _assessmentsModel.GetDataList("Questions", "question_standard");

        if (questionStandards != null && questionStandards.Any())
        {
            _view.QuestionStandards = new HashSet<string>(questionStandards).ToList();
        }
        else
        {
            _view.Message("No question standards found, please add a new question standard.");
        }

    }

    // Fills the maturity list of the view with all possible maturity standards. If none are found, message is sent to the view.
    public void InitializeMaturityStandards(object sender, EventArgs e)
    {
        List<string> maturityStandards = _assessmentsModel.GetDataList("MaturityAnswers", "m_answer_standard");

        if (maturityStandards != null && maturityStandards.Any())
        {
            _view.MaturityStandards = new HashSet<string>(maturityStandards).ToList();
        }
        else
        {
            _view.Message("No maturity standards found, please add a new question standard.");
        }
    }

    // Fills the compliance list of the view with all possible compliance standards. If none are found, message is sent to the view.
    public void InitializeComplianceStandards(object sender, EventArgs e)
    {
        List<string> complianceStandards = _assessmentsModel.GetDataList("ComplianceAnswers", "c_answer_standard");

        if (complianceStandards != null && complianceStandards.Any())
        {
            _view.ComplianceStandards = new HashSet<string>(complianceStandards).ToList();
        }
        else
        {
            _view.Message("No compliance standards found, please add a new question standard.");
        }
    }
}

DetailScreenUserControl.cs This class dynamically adds controls to ittself based on the amount of checkboxes checked in 'InitializeUserControl' which is subsribed to the 'InitializingUserControl' event from the View.

public partial class DetailScreenUserControl : UserControl
{
    private readonly IDetailScreenView _view;
    private List<ComboBox> maturityInput = new List<ComboBox>();
    private List<ComboBox> complianceInput = new List<ComboBox>();

    // Initialize user control with IDetailScreenView. Subscribe to necessary events.
    public DetailScreenUserControl(IDetailScreenView view)
    {
        InitializeComponent();
        _view = view;
        _view.InitializingUserControl += InitializeUserControl;
    }

    // Initializes the user control for the detail screen.
    public void InitializeUserControl(object sender, EventArgs e)
    {
        List<string> qStandards = _view.SelectedQuestionStandards;
        this.Controls.Clear();

        for (int  i = 0; i < qStandards.Count; i++)
        {
            Panel inputPanel = new Panel();
            inputPanel.Location = new Point(0, i * 115);
            inputPanel.AutoSize = true;
            inputPanel.BackColor = Color.AliceBlue;
            this.Controls.Add(inputPanel);

            Label qs_label = new Label();
            qs_label.Location = new Point(0, 0);
            qs_label.Font = new Font("Arial", 12F, FontStyle.Regular);
            qs_label.Size = new Size(150, 18);
            qs_label.Text = qStandards[i].ToString();
            inputPanel.Controls.Add(qs_label);

            Label m_label = new Label();
            m_label.Location = new Point(0, 35);
            m_label.Font = new Font("Arial", 12F, FontStyle.Regular);
            m_label.Size = new Size(150, 18);
            m_label.Text = "Maturity standard";
            inputPanel.Controls.Add(m_label);

            Label c_label = new Label();
            c_label.Location = new Point(0, 70);
            c_label.Font = new Font("Arial", 12F, FontStyle.Regular);
            c_label.Size = new Size(160, 18);
            c_label.Text = "Compliance standard";
            inputPanel.Controls.Add(c_label);

            ComboBox m_input = new ComboBox();
            m_input.Location = new Point(160, 35);
            m_input.Font = new Font("Arial", 10F, FontStyle.Regular);
            m_input.Size = new Size(165, 22);
            m_input.DropDownStyle = ComboBoxStyle.DropDownList;
            maturityInput.Add(m_input);
            inputPanel.Controls.Add(m_input);

            ComboBox c_input = new ComboBox();
            c_input.Location = new Point(160, 70);
            c_input.Font = new Font("Arial", 10F, FontStyle.Regular);
            c_input.Size = new Size(165, 22);
            c_input.DropDownStyle = ComboBoxStyle.DropDownList;
            complianceInput.Add(c_input);
            inputPanel.Controls.Add(c_input);

        }

        Button saveAssessmentButton = new Button();
        saveAssessmentButton.Location = new Point(235, qStandards.Count * 115);
        saveAssessmentButton.Text = "Save assessment";
        this.Controls.Add(saveAssessmentButton);

        _view.AddUserControl();
    }

    // Sets the data source for the maturity comboboxes.
    public void InitializeMaturityInput(List<string> input)
    {
        for(int i = 0; i < maturityInput.Count; i++)
        {
            maturityInput[i].DataSource = new List<string>(input);
        }
    }

    // Sets the data source for the compliance comboboxes.
    public void InitializeComplianceInput(List<string> input)
    {
        for (int i = 0; i < complianceInput.Count; i++)
        {
            complianceInput[i].DataSource = new List<string>(input);
        }
    }


}
\$\endgroup\$
  • 1
    \$\begingroup\$ You may want to consider data binding a collection which implements IBindingList and contains the selected question standards to the user control. You could then code the user control to handle ListChanged events to add/remove/update your items rather than clearing and re-populating from scratch every time. \$\endgroup\$ – Dan Lyons Oct 12 '16 at 18:00
  • 1
    \$\begingroup\$ A very quick glance at StackOverflow tells me: 1. Use SuspendLayout and ResumeLayout 2. Use AddRange if you can 3. Don't use an auto scrolling pane as the container. Finally, this thread kinda-maybe sounds like it might be helpful: Best way to suspend control events in Windows Forms? \$\endgroup\$ – radarbob Oct 18 '16 at 14:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.