Unit test a dynamically populating user control in WinForms - c#

I have a WinForms project with MVP pattern (passive view) implemented.
I think that I have a problem with the pattern when it comes to a user control, which I figured out during unit testing
I have a user control that I put on my form as a result of an event fired in my view. That user control adds a certain amount of labels, textboxes, etc. to itsself based on a number it gets from the view. Finally, it tells the view to add the user control to the view.
I want to unit test the logic in this class, since that is what I think is most important to test. I just do not know how to do this, since there is both logic and form controls in this class. I am currently using Moq for creating my unit tests.
I would normally create a Mock object to represent the view and then test the implementation of the methods in the object to be tested in isolation. However, since I create controls in this class, I don't think I can test this like this (without including the .Forms library that is).
I hope someone knows a solution.
EDIT: I have been trying to separate my logic from the control manipulation, but I am struggling with a function from a different user control I have posted below the original user control code. Since I loop through a list of controls, I dont know how to separate this into just logic and just control handling.
User control code
public partial class DetailScreenUserControl : UserControl
{
// Private members.
private readonly IDetailScreenView _view;
private List<ComboBox> maturityInput = new List<ComboBox>();
private List<ComboBox> complianceInput = new List<ComboBox>();
// Public members.
public List<string> MaturityInput
{
get
{
var list = new List<string>();
for (int i = 0; i < maturityInput.Count; i++)
{
list.Add(maturityInput[i].Text);
}
return list;
}
set
{
for (int i = 0; i < maturityInput.Count; i++)
{
maturityInput[i].DataSource = new List<string>(value);
}
}
}
public List<string> ComplianceInput
{
get
{
var list = new List<string>();
for (int i = 0; i < complianceInput.Count; i++)
{
list.Add(complianceInput[i].Text);
}
return list;
}
set
{
for (int i = 0; i < complianceInput.Count; i++)
{
complianceInput[i].DataSource = new List<string>(value);
}
}
}
// 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;
Controls.Clear();
maturityInput.Clear();
complianceInput.Clear();
int inputSeparation = Height / 2;
int spacing = Width / 20;
Size = new Size(_view.RightUserControlBoundary - Location.X, Size.Height);
for (int i = 0; i < qStandards.Count; i++)
{
Panel inputPanel = new Panel();
inputPanel.BackColor = Color.AliceBlue;
inputPanel.Location = new Point(0, i * inputSeparation);
inputPanel.Size = new Size(Width - spacing, inputSeparation);
Controls.Add(inputPanel);
Label qs_label = new Label();
qs_label.AutoSize = true;
qs_label.Location = new Point(0, 0);
qs_label.Font = new Font("Arial", 12F, FontStyle.Bold);
qs_label.AutoSize = true;
qs_label.Text = qStandards[i].ToString();
inputPanel.Controls.Add(qs_label);
Label m_label = new Label();
m_label.AutoSize = true;
m_label.Location = new Point(0, qs_label.Bounds.Bottom + qs_label.Height / 2);
m_label.Font = new Font("Arial", 12F, FontStyle.Regular);
m_label.Text = "Maturity standard";
inputPanel.Controls.Add(m_label);
Label c_label = new Label();
c_label.AutoSize = true;
c_label.Location = new Point(0, m_label.Bounds.Bottom + qs_label.Height / 2);
c_label.Font = new Font("Arial", 12F, FontStyle.Regular);
c_label.Text = "Compliance standard";
inputPanel.Controls.Add(c_label);
ComboBox m_input = new ComboBox();
m_input.AutoSize = true;
m_input.Location = new Point(c_label.Bounds.Right + 2 * spacing, m_label.Bounds.Top);
m_input.Font = new Font("Arial", 10F, FontStyle.Regular);
m_input.DropDownStyle = ComboBoxStyle.DropDownList;
m_input.Size = new Size(inputPanel.Size.Width - m_input.Bounds.Left, spacing);
maturityInput.Add(m_input);
inputPanel.Controls.Add(m_input);
ComboBox c_input = new ComboBox();
c_input.AutoSize = true;
c_input.Location = new Point(c_label.Bounds.Right + 2 * spacing, c_label.Bounds.Top);
c_input.Font = new Font("Arial", 10F, FontStyle.Regular);
c_input.DropDownStyle = ComboBoxStyle.DropDownList;
c_input.Size = new Size(inputPanel.Size.Width - c_input.Bounds.Left, spacing);
complianceInput.Add(c_input);
inputPanel.Controls.Add(c_input);
}
if(qStandards.Count != 0)
{
saveAssessmentButton.BackColor = System.Drawing.SystemColors.ButtonHighlight;
Controls.Add(saveAssessmentButton);
saveAssessmentButton.Location = new Point(this.Size.Width - saveAssessmentButton.Width - spacing, qStandards.Count * inputSeparation);
}
_view.AddUserControl();
}
// Tells the view to save the assessment.
private void saveAssessmentButton_Click(object sender, EventArgs e)
{
_view.SaveAssessmentButtonClicked();
}
}
Other user control function('answers' is the list of controls)
public void SaveResults()
{
results = new List<string>();
int questionNr = 0;
for (int p = 0; p < questions.Count; p++)
{
for (int i = 0; i < questions[p].Count; i++)
{
bool unanswered = true;
results.Add(questions[p][i]);
for (int j = 1; j <= maturityAnswers[p].Count; j++)
{
var radioButton = (RadioButton)answers[questionNr][j];
if (radioButton.Checked)
{
results.Add(answers[questionNr][j].Text);
unanswered = false;
}
}
if (unanswered == true)
{
results.Add("");
}
unanswered = true;
for (int j = maturityAnswers[p].Count + 1; j <= (maturityAnswers[p].Count + complianceAnswers[p].Count); j++)
{
var radioButton = (RadioButton)answers[questionNr][j];
if (radioButton.Checked)
{
results.Add(answers[questionNr][j].Text);
unanswered = false;
}
}
if (unanswered == true)
{
results.Add("");
}
results.Add(answers[questionNr][0].Text.Replace("'", "''"));
questionNr++;
}
}

I want to unit test the logic in this class, since that is what I
think is most important to test. I just do not know how to do this,
since there is both logic and form controls in this class
So separate them to different classes and test the class which contains only logic

Related

How can I access to my Grid table from my private method

I started yesterday a new proyect in c# with WPF. My first time with it.
I'm trying to do the tictactoe game with graphical interface so i create the grid and i use bottons to change the state (it's not finish yet).
Here is the declaration of the class:
public partial class juego : Window
{
private juego tab;
public juego( int size)
{
InitializeComponent();
this.tab = CreateDynamicWPFGrid(size);
}
Here is the method that created the grid.
public juego CreateDynamicWPFGrid(int size)
{
Grid DynamicGrid = new Grid();
DynamicGrid.Name = "GridTablero";
DynamicGrid.Width = 400;
DynamicGrid.HorizontalAlignment = HorizontalAlignment.Left;
DynamicGrid.VerticalAlignment = VerticalAlignment.Top;
DynamicGrid.ShowGridLines = true;
DynamicGrid.Background = new SolidColorBrush(Colors.LightSteelBlue);
for (int i = 0; i < size; i++)
{
ColumnDefinition gridCol1 = new ColumnDefinition();
DynamicGrid.ColumnDefinitions.Add(gridCol1);
RowDefinition gridRow1 = new RowDefinition();
gridRow1.Height = new GridLength(45);
DynamicGrid.RowDefinitions.Add(gridRow1);
}
for (int fila = 0; fila < DynamicGrid.RowDefinitions.Count; fila++)
{
for (int columna = 0; columna < DynamicGrid.RowDefinitions.Count; columna++)
{
System.Windows.Controls.Button newBtn = new Button();
newBtn.Content = fila.ToString() + "_" + columna.ToString();
newBtn.Name = "Button" + fila.ToString() + "_" + columna.ToString();
newBtn.SetValue(Grid.ColumnProperty, columna);
newBtn.SetValue(Grid.RowProperty, fila);
newBtn.Click += new RoutedEventHandler(button_Click);
DynamicGrid.Children.Add(newBtn);
}
}
tablero.Content = DynamicGrid;
return tablero;
}
So the thing is that i want to iterate over the grid and then, count the buttons which content means if they are X, O or white.
I tried to use in my private method something like tab.Content but i really don't have any idea.
Anyways, i would like to know if this it is even possible.
After all, i come up with a solution by myselft.
So, i changed some lines in the class.
public partial class juego : Window
{
private ArrayList jugadores;
public juego(ArrayList jugadores, int size)
{
InitializeComponent();
tablero.Content = CreateDynamicWPFGrid(size);
}
Then i changed the return from the CreateDynamicWPFGrid:
return dynamicGrid;
And then, one method that i call when i click on buttons.
Grid boardValidar = tablero.Content as Grid;
Button[,] botones = new Button[boardValidar.ColumnDefinitions.Count, boardValidar.ColumnDefinitions.Count];
var buttons = boardValidar.Children.Cast<Button>();
for (int i = 0; i < boardValidar.ColumnDefinitions.Count; i++)
{
for (int j = 0; j < boardValidar.RowDefinitions.Count; j++)
{
botones[i, j] = buttons.Where(x => Grid.GetRow(x) == j && Grid.GetColumn(x) == i).FirstOrDefault();
}
}
So, i cast the grid and then, i do the same with all the buttons.
After this i use the linq to put in the array and that's all.
I know my code it's no the best but i got what i wanted.

couldn't change active tabPage with button c#

I have a MetroTabControl named mtcNewExpTabControl with four pages(mtpSettings, mtpNewExp, mtpTempImages, mtpCompareImages). I am starting New Experiment on mtpNewExp page and get image after clicking "StartExperiment" button. User can get many images. After completion of experiment, I add images to imageList1.Images.
When I click on the mtpTempImages tabPage mtcNewExpTabControl_SelectedIndexChanged triggered and images are displayed with combobox named with imageList1.Images.Keys[i]. I can show many combobox and picturebox pairs on mtpTempImages.
On mtpTempImages there is also a mtbCompareImages button. User can check checkboxes next to the picturebox named same with checboxName. After checking all the checkboxes needed to compared, user will click on the "Compare Images" button. I want to change active tabPage to mtpCompareImages without clicking on the tab. mtbCompareImages button should be enough for that. I can show images on mtpTempImages but I could not succeded that on mtpCompareImages tabpage. Active tabPage also do not change to mtpCompareImages tabPage. What should I do?
List<MetroCheckBox> cbxTempImages = new List<MetroCheckBox>();
List<MetroCheckBox> cbxCompareImages = new List<MetroCheckBox>();
List<PictureBox> pbxTempImages = new List<PictureBox>();
List<PictureBox> pbxCompareImages = new List<PictureBox>();
private void mtbCompareImages_Click(object sender, EventArgs e)
{
for (int i = 0; i < cbxTempImages.Count(); i++)
{
if (cbxTempImages[i].Checked == true)
{
imageListChecked.Images.Add(pbxTempImages[i].Name, pbxTempImages[i].Image);
}
}
this.mtcNewExpTabControl.SelectedTab = mtpCompareImages;
}
private void mtcNewExpTabControl_SelectedIndexChanged(object sender, EventArgs e)
{
if((MetroTabPage)this.mtcNewExpTabControl.SelectedTab == mtpTempImages)
{
for (int i = 0; i < imageList1.Images.Count; i++)
{
cbxTempImages.Add(new MetroCheckBox());
cbxTempImages[i].Name = imageList1.Images.Keys[i].ToString();
cbxTempImages[i].Size = new Size(15, 15);
cbxTempImages[i].BackColor = Color.Transparent;
cbxTempImages[i].Location = new Point(x, y);
//PictureBox pic = new PictureBox();
pbxTempImages.Add(new PictureBox());
pbxTempImages[i].Name = imageList1.Images.Keys[i].ToString();
pbxTempImages[i].Image = imageList1.Images[i];
pbxTempImages[i].SizeMode = PictureBoxSizeMode.StretchImage;
pbxTempImages[i].Location = new Point(x + 15, y);
x += 120;
if (x > this.pnlTempImages.Width - 120)
{
x = 10; y += 100;
}
this.pnlTempImages.Controls.Add(cbxTempImages[i]);
this.pnlTempImages.Controls.Add(pbxTempImages[i]);
}
}
else if((MetroTabPage)this.mtcNewExpTabControl.SelectedTab == mtpCompareImages)
{
x = 10; y = 10;
for (int i = 0; i < imageListChecked.Images.Count; i++)
{
cbxCompareImages.Add(new MetroCheckBox());
//MetroCheckBox cbxCI = new MetroCheckBox();
cbxCompareImages[i].Name = imageListChecked.Images.Keys[i].ToString();
cbxCompareImages[i].Size = new Size(15, 15);
cbxCompareImages[i].BackColor = Color.Transparent;
cbxCompareImages[i].Location = new Point(x, y);
//PictureBox picCI = new PictureBox();
pbxCompareImages.Add(new PictureBox());
pbxCompareImages[i].Name = imageListChecked.Images.Keys[i].ToString();
pbxCompareImages[i].Image = imageListChecked.Images[i];
pbxCompareImages[i].SizeMode = PictureBoxSizeMode.StretchImage;
pbxCompareImages[i].Location = new Point(x + 15, y);
x += 120;
if (x > this.pnlCompareImages.Width - 120)
{
x = 10; y += 100;
}
this.pnlCompareImages.Controls.Add(cbxCompareImages[i]);
this.pnlCompareImages.Controls.Add(pbxCompareImages[i]);
}
}
InitializeComponent();
}

(C#) Create user control components with for loop and put those components in a list

Of course it's easy to use designer. But, in case for plenty of controls, it hurts to use designer. I'm trying to create a Sudoku table 9 x 9, and need 81 text box with exactly same size. I think, it will be easier to use for loop.
My code so far is like this:
class SudokuTable
{
private List<List<TextBox>> table;
public SudokuTable()
{
initializeComponent();
}
private void initializeComponent()
{
List<List<TextBox>> newTable = new List<List<TextBox>>();
int sz = 30;
int gap = 3;
int x = gap, y = gap;
for (int row = 0; row < 9; row++)
{
x = gap;
List<TextBox> TextBoxes = new List<TextBox>();
for (int col = 0; col < 9; col++)
{
System.Drawing.Size size = new System.Drawing.Size(sz, sz);
System.Drawing.Point location = new System.Drawing.Point(x, y);
System.Drawing.Font font = new System.Drawing.Font("Tahoma", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
TextBox box = new TextBox();
box.Location = location;
box.Size = size;
box.TextAlign = HorizontalAlignment.Center;
box.Font = font;
box.MaxLength = 1;
TextBoxes.Add(box);
x += ((col + 1) % 3 == 0 && (col + 1) < 9) ? sz + gap : sz;
}
newTable.Add(TextBoxes);
y += ((row + 1) % 3 == 0 && (row + 1) < 9) ? sz + gap : sz;
}
table = newTable;
}
public void addSudoku(System.Windows.Forms.Form form)
{
form.SuspendLayout();
foreach (List<TextBox> row in table)
{
foreach (TextBox col in row)
{
form.Controls.Add(col);
}
}
form.PerformLayout();
}
}
And add that table in Main Form with this code:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
SudokuTable st = new SudokuTable();
this.ClientSize = st.getSize();
st.addSudoku(this);
}
}
So far, it works fine. Here's the result:
My problem is, my SudokuTable class can't behave like normal user control. What I mean by normal user control is like when I create a user control via Project --> Add Class --> User Control. My class can't appear in the toolbox.
Is there anyway to create user control components with for loop and make it to behave like normal user control. So I can treat my created control like another control (like add it to Main Form with designer)?
it seems to me you should create new UserControl and incapsulate all this logic in it. Then build solution and UserControl should apear in your toolbox.

Get the values from dynamic NumericUpDown in C#

I have created dynamic NumericUpDown. I want to save the values that the user enters. How can i do that. After I am saving the values I want to do some time manipulation with them. Knowing I do not know the number of NumericUpDown the user wants each time?
// To set up the location for the NumericUpDown
int xCoor;
int yCoor;
Random coor = new Random();
// Button to create the NumericUpDown
private void btnRun_Click(object sender, EventArgs e)
{
/// I am assuming that the user choice is 8
int value = 8;
//this calls the method which is going to create NumericUpDown
this.AddNewNumrical(value);
}
//Method to Create NumericUpDown
private void AddNewNumrical(int numiraclNew)
{
for (int x = 0; x < numiraclNew; x++)
{
for (int y = 0; y < 1; y++)
{
NumericUpDown numiNumber = new NumericUpDown();
xCoor = coor.Next(0, 700);
yCoor = coor.Next(0, 710);
numiNumber.Location = new Point(xCoor, yCoor);
numiNumber.Size = new System.Drawing.Size(50, 15);
numiNumber.Maximum = 1000;
numiNumber.Minimum = 1;
this.pnlNodes.Controls.Add(numiNumber);
}
}
}
Just store your new NumericUpDown control in another list! You could also search the Controls collection for NumericUpDown controls but you might pick up some stuff you don't want:
List<NumericUpDown> numberControls = new List<NumericUpDown>();
//Method to Create NumericUpDown
private void AddNewNumrical(int numiraclNew)
{
for (int x = 0; x < numiraclNew; x++)
{
NumericUpDown numiNumber = new NumericUpDown();
xCoor = coor.Next(0, 700);
yCoor = coor.Next(0, 710);
numiNumber.Location = new Point(xCoor, yCoor);
numiNumber.Size = new System.Drawing.Size(50, 15);
numiNumber.Maximum = 1000;
numiNumber.Minimum = 1;
numberControls.Add(numiNumber); //Save the control off for later
this.pnlNodes.Controls.Add(numiNumber);
}
}
Then you can use it later to do whatever you want:
private void Foo()
{
foreach (NumericUpDown userSelection in numberControls)
{
//Do whatever with userSelection.Value
}
}

derived class problem using interface

I have a interface
interface Dot
{
// protected Random r = new Random();
void createdot(Rectangle clientrectangle, Control.ControlCollection Controls);
}
and I use this interface as a base class for my derived classes as stated
public class BlueDot : Dot
{
public List<Label> bluedot = new List<Label>();
Random r = new Random();
public void createdot(Rectangle ClientRectangle, Control.ControlCollection Controls)
{
for (int i = 0; i < 5; i++)
{
var temp = new Label();
temp.Location = new Point(r.Next(ClientRectangle.Right - 10), r.Next(ClientRectangle.Bottom - 20));
temp.Text = "?";
temp.Width = 10;
temp.Height = 10;
temp.ForeColor = System.Drawing.Color.Blue;
temp.BackColor = System.Drawing.Color.White;
Controls.Add(temp);
temp.Visible = true;
temp.Show();
bluedot.Add(temp);
}
}
}
and
public class RedDot:Dot
{
public List<Label> reddot = new List<Label>();
Random r = new Random();
public void createdot(Rectangle Clientrectangle,Control.ControlCollection Controls)
{
for (int z = 0; z < 10; z++)
{
var temp2 = new Label();
temp2.Location = new Point(r.Next(Clientrectangle.Right - 10), r.Next(Clientrectangle.Bottom - 20));
temp2.Text = "?";
temp2.Width = 10;
temp2.Height = 10;
temp2.ForeColor = System.Drawing.Color.Red;
temp2.BackColor = System.Drawing.Color.White;
Controls.Add(temp2);
temp2.Show();
reddot.Add(temp2);
}
}
and they are called here
BlueDot bdot = new BlueDot();
RedDot rdot = new RedDot();
private void Form1_Load(object sender, EventArgs e)
{
this.Activate();
bdot.createdot(this.ClientRectangle,this.Controls);
rdot.createdot(this.ClientRectangle, this.Controls);
}
Why is it that I keep getting only 5 red dots even if the loop performs 10 iterations?
here is the sample output https://www.facebook.com/photo.php?fbid=2372861522202&set=a.1600508493859.85328.1270463960&type=1 , i just cant figure out what happened to the other 5 red dots, it should be 10 red dots....
There is no inheritance problem here. The problem is the random number generator.
Try this line in your code:
temp2.Location = new Point(10 * z, 10 * z);
Replacing
temp2.Location = new Point(r.Next(Clientrectangle.Right - 10), r.Next(Clientrectangle.Bottom - 20));
You'll see your 5 blue "?"-labels and your 10 red "?"-labels
To solve the problem of the weak random number generator try seeding your random number generator. Example:
Random r = new Random((int)DateTime.Now.Ticks);
I am not that sure, but why aren't you making your temp2 visible ????
temp2.Visible=true
If this dowsn't work can you provide a screen shot of both your output. Sometimes due to the size of the window one dot may reside over another. I mean they actually overlap. Seeing your output may help to sort out your problem

Categories