How to save data and display it when I have some constraints - c#

Its hard to specify a title...
I breaking my head to figure out this problem,
I have packet that including "data" "time" and "id".
I need to save it in some data structure and display only the data in some way on the form (maybe a datagridview),
but I need the ability to get the rest packet info (time and id) when I'm clicking on the displayed data.
For example:
0110 1110 0101 0001
The first data id is 9 and the time is 2222. When I click on the first data (0110) I need to display (lets say in lable on the form) id = 9 and time = 2222.
One more thing, the data must be displayed like the way in the above example (in row with space between the data.
Edit:
I forgot something important.
If I use databinding there is option to change the data location on the grid (based on some packet info) from some cell/row to another? if not maybe databinding its not good here.

If I understand what you are trying to do, using a DataGridView, try this:
// DataGridView
Databing the data source to the DataGridView (use a list of your packet for that)
---------------------------------
| DATA 1 | DATA 2 | DATA 3 | ... (Header)
---------------------------------
| 0110 | 1110 | 0101 | ... (Data)
---------------------------------
Add a event handler to the DataGridView CellContentClick, just like this:
private void myDataGrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex > -1 && e.ColumnIndex > -1) // A row and cell was selected
{
var packet = myDataGrid.Rows[e.RowIndex].DataBoundItem as Packet;
if (packet != null)
{
// Display packet information
}
}
}
Hope it helps.

Make your packet a class where data, time and id are private members and DisplayData is a public property. To access the content of the private members from outside, use properties with the [Browsable(false)] attribute:
public class Packet
{
private int data, time, id;
public string DisplayData {get {return FunctionToFormatDataToMyNeeds(data); }}
// ...
[Browsable(false)]
public int Time{get{return time;}}
}
Bind a list of those objects to the data source of your DataGridView.

Related

How to sum 2 columns within a row of datagridView

Hello guys? i'am working on Windows application form c# and I have 4 Columns in my datagridView, lets say i have 3 rows just like this
-------------------------------
|Grade1|Grade2|Average|Remarks|
|------|------|-------|-------|
| 85| 80| 82.5|PASSED |
| 76| 86| 81|PASSED |
| 75| 72| 73.5|FAILED |
-------------------------------
Now my question is it possible if i click a compute button it will compute each rows of the Column"Grade1" and Column"Grade2" the computation is
Grade1 + Grade2 = Average and Average = Average/2
if it's below 74 the remarks column automatically Have a "FAILED"
if it's above 75 the remarks column automatically Have a "PASSED"
Does anyone have a example code or a link that can help me to do this? Thank you so much!
you can enumerate thru all the rows by doing
foreach(DataGridViewRow row in dataGridView.Rows)
you can get or set the value of cells in that particular row by doing
row["column name"].Value
you can attach a click event to Button.Click (either in code by using .Click += handler or do it in the designer)
To assemble all those pieces and come up with a working solution will be your exercise. Good luck :)
private void CalculateButton_Click(object sender, EventArgs e)
{
foreach (DataGridViewRow row in dgvGrades.Rows)
{
if (decimal.TryParse(row.Cells["Grade1"]?.Value?.ToString(), out decimal Grade1)
&& decimal.TryParse(row.Cells["Grade2"]?.Value?.ToString(), out decimal Grade2))
{
var avg = (Grade1 + Grade2) / 2;
row.Cells["Average"].Value = avg;
if (avg < 74)
{
row.Cells["Remarks"].Value = "FAILED";
}
else
{
row.Cells["Remarks"].Value = "PASSED";
}
}
}
}
I would just add members to the class that is populating the array or list that is used to populate the grid. The getters for the average and pass/fail fields can handle the logic.

Is there a way to create smth like a Scenario Outline in Gherkin that is executed for multiple values without restarting the Scenario?

I am a test designer and I am creating .feature files.
We are using Visual Studio + Specflow, the code is written in C#.
I have a following test case:
Background:
Given something
And something
And something
Scenario: Scenario name
When I set the 'X' value' in the Y field
Then The 'X' value is displayed in 'Somewhere'
My problem is:
I need to check 20 values.
But if I use Scenario Outline with Examples, then it will be run from the start for each 'X' value I use, all Givens will be executed everytime.
This will take long while the test is quite simple and running the When+Then combination for each value after the background is run could be enough for the test purpose.
Is there a way to check multiple values in Gherkin without using the Scenario Outlines, having a possibility to just run a When and Then combination multiple times to check each value?
Appreciate any help with this matter
You could use a datatable as input, and implement your step in such a way that it parses the datatable. You can then perform assertions on each table element.
SpecFlow DataTables Documentation
Given I test the following outcomes
| value | field | display_location |
| X | Y1 | Somewhere |
| y | Y2 | Somewhere Els |
By having an instance variable to hold the test values, you can perform actions and verifications in other steps on the dataset.
You have two steps - one to set some value to the field, and another to check if some field has given value:
[When(#"I set the '(.*)' value in the (.*) field")]
public void WhenISetTheValueInTheField(string value, string field)
{
// ...
}
[Then(#"The '(.*)' value is displayed in (.*) field")]
public void ThenTheValueIsDisplayedInField(string value, string field)
{
// ...
}
With Specflow you can call steps from Step Definitions. So basically you need a high-level step which will explain your high-level intent - verification that all given input values are displayed correctly. To pass values and verification data you can use a table:
Then all input values are displayed correctly:
| InputValue | InputField | DisplayedValue | DisplayField |
| X | Y | X | Somewhere |
| Z | Y | Z | SomewhereElse |
And inside this step, you can call steps which you already have for each set of data:
[Then(#"all input values are displayed correctly:")]
public void ThenAllInputValuesAreDisplayedCorrectly(Table table)
{
foreach (var row in table.Rows)
{
var inputValue = row["InputValue"];
var inputField = row["InputField"];
When($"I set the '{inputValue}' value in the {inputField} field");
var displayedValue = row["DisplayedValue"];
var displayField = row["DisplayField"];
Then($"The '{displayedValue}' value is displayed in {displayField} field");
}
}
The Nice thing here is that beside high-level step, you will see each step with its parameters in the output.

How to make formula between 2 datagridview in C# [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
How to connect two data grid views so that their data is updated automatically. E.g. When coloumb1 in DataGridView1 increases then Coloumb2 in DatagridView2.
Sharing my idea to implement a solution where changing a value in a cell updates another value
First, Make a class like CellRelation - its purpose is to establish a relation between 2 Grid cells.
class CellRelation
{
DataGridCell SourceCell;
DataGridCell DestinationCell;
Func<DataGridCell, DataGridCell, decimal> Formula;
}
Secondly, Initialization
Populate your Grids as you currently do
For all the grid cells where you wish to have a formula, create an instance of CellRelation & add it do a collection - CellRelations
When you create an instance of CellRelation -> provide it with source cell that, destination cell and a delegate.
for example in your case if you would like to calculate remaining inventory -
Source Cell will be Sold Inventry, destination cell will be remainig inventory cell.
Formula (delegate): I have thought of this delegate to expect 2 grid cells as inputs and give the result as decimal.
the the input grid cells will be "TotalInventoryCell" & "SoldInvenoryCell"
and this delegate will be a funcition to subtract the values of the given cells. The return of the delegate will be a decimal value that you can use to update the remainig inventory cell
Thirdly, the event of updating a cell in grid one.
When the value of the cell changes in the grid, handle the appropriate event. On this event handler, traverse the collection – CellRelations, to find if there is a dependent cell whose value needs to be updated as per the formula entered.
If you find an entry for the cell getting updated, execute the delegate(formula) and use the decimal value returned by the delegate(formula) to update the destination Cell’s value
Incase you think some part is unclear please let me know, I'll try to provide a sample
Working Sample
I made a short working sample (without dataset) to demonstrate my approach.
I made a single datagridview with one row & 3 columns - Total, Sold & Remaining
So every time a change is made to Sold cell, the remaining item gets update.
I made it with single grid but the same can be extended for 2 grids also.
It has a lot of scope for improvement, especially the expression part, ideally it should be able to evaluate an expression tree.
class CellRelation
{
public DataGridViewCell SourceCell;
public DataGridViewCell DestinationCell;
public CellFormula Formula;
}
class CellFormula
{
public Func<DataGridViewCell, DataGridViewCell, decimal> Operator;
public DataGridViewCell Operand1;
public DataGridViewCell Operand2;
public decimal Evaluate()
{
return Operator(Operand1, Operand2);
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
List<CellRelation> cellRelations = new List<CellRelation>();
private void Initialise_Click(object sender, EventArgs e)
{
var soldCell = this.dataGridView1[1, 0];
var remainingCell = this.dataGridView1[2, 0];
var totalCell = this.dataGridView1[0, 0];
// datagid values --- In your case this is from a dataset
totalCell.Value = 10;
soldCell.Value = 0;
remainingCell.Value = 10;
// initialise the relation / formula
CellRelation relation = new CellRelation();
relation.SourceCell = soldCell;
relation.DestinationCell = remainingCell; // thats the dependent cell
relation.Formula = new CellFormula();
// here is a sample of subtraction formula : Subtracting Sold items for total items
relation.Formula.Operator = new Func<DataGridViewCell, DataGridViewCell, decimal>((p, v) => { return ((decimal.Parse(p.Value.ToString()))) - ((decimal.Parse(v.Value.ToString()))); });
relation.Formula.Operand1 = totalCell;
relation.Formula.Operand2 = soldCell;
cellRelations.Add(relation);
}
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
//look up if there is an destination cell for the cell being updated
var cellReln = cellRelations.FirstOrDefault(item => item.SourceCell.RowIndex == e.RowIndex && item.SourceCell.ColumnIndex == e.ColumnIndex);
if (cellReln != null)
{
cellReln.DestinationCell.Value = cellReln.Formula.Evaluate();
}
}
}
Edit : Please note - the approach I have suggested is with CellRelation & CellFormula has properties with type DataGridViewCell. Therefore its tightly bound to the UI technology (winform in this case).
Ideally such a solution should be UI technology independent. Incase you need a sample where this sits in a separate business layer, do write me a comment.

How to fill 56 comboBox with loop or something

I have 56 comboBox in my program and I need to fill all of them with the same information. The fastes way that I found is creating a private function for fill the comboBox. And I put 56 times the same function.
But my question is... I can do a loop for fill this 56 comboBox?
Assuming this question is about Windows Forms. The best way to do it is probably to have the similar naming convention for all ComboBox controls you're willing to fill with the same data. Fill them with items within for-loop, adding different suffix to the control you want to find before adding data.
// Lets say you have 56 ComboBox controls with names like : cbMyComboBox_1, cbMyComboBox_2, ..., cbMyComboBox_56
for (int i = 1; i <= 56; i++)
{
ComboBox comboBox = (ComboBox)this.Controls.Find
(string.Format("cbMyComboBox_{0}", i), true)[0];
ComboBoxFill(comboBox);
}
private void ComboBoxFill(ComboBox comboBox)
{
// Fill that ComboBox with data here
}
Of course you can.
foreach (var c in Controls)
{
if (c is ComboBox)
((ComboBox)c).Text = "I'm a combobox~";
}
try adding the same class to all 56 combobox, and create a function to populate then like:
$(".class").html(value of the options);

Synchronising TableLayoutPanels

I have several TableLayoutPanels, each of which displays a category of name/value information in two columns - one with informational labels, and one with data labels.
In each one, I've set the first column to autosize and right-aligned all the labels, which works just fine. However, it works separately to each TableLayoutPanel (obviously), and looks something like this:
TableLayoutPanel 1:
+--------+--------+
| Name 1 | Data 1 |
+--------+--------+
| Name 2 | Data 2 |
+--------+--------+
| Name 3 | Data 3 |
+--------+--------+
TableLayoutPanel 2:
+------------------+--------+
| Long Name 1 | Data 1 |
+------------------+--------+
| Very Long Name 2 | Data 2 |
+------------------+--------+
| Long Name 3 | Data 3 |
+------------------+--------+
I'm looking for a way to consider all of the name labels when autosizing all of the first columns, so it looks like this:
TableLayoutPanel 1:
+------------------+--------+
| Name 1 | Data 1 |
+------------------+--------+
| Name 2 | Data 2 |
+------------------+--------+
| Name 3 | Data 3 |
+------------------+--------+
TableLayoutPanel 2:
+------------------+--------+
| Long Name 1 | Data 1 |
+------------------+--------+
| Very Long Name 2 | Data 2 |
+------------------+--------+
| Long Name 3 | Data 3 |
+------------------+--------+
I can't put all the data into one table, because each table represents a different category of information, and is inside a custom control with a collection of collapsible panels (so you can show or hide each category separately).
I've been trying to achieve this by overriding the container controls OnLayout(), setting all of the TableLayoutPanels' first columns to autosize, getting all of their widths, finding the maximum, and then settings all of their first columns to a fixed size of the greatest width. This works, but looks horrible every time layout occurs as all the columns jump to autosize and then back to fixed size.
I'm assuming I'm going to have to hook ControlAdded and ControlRemoved for each table, and then SizeChanged for each child control, to know when the size of any child control changed, and then manually set the column width somehow, but I'm not sure how to reliably get the correct widths.
I tried a variation of the first method - using GetPreferredSize() on all the controls in the first columns, to attempt to find the largest width, and then setting all first columns to a fixed size, but it seemed to return widths that were slightly to small. Should I be applying some extra spacing?
Does anyone know any way of asking the TableLayoutPanel to perform autosize calculations without it actually applying them visually? Or perhaps, lying to the tables to 'pretend' that there is a control of a certain width, just so it takes it into account? I can't add actual controls, since it will then want to allocate more cells for them. I tried looking at the source with ILSpy, but well, it isn't pretty. Seems most of the work is done by TableLayout class, which is, of course, internal, and I couldn't follow what it was doing.
Thanks for any ideas...
You can use the Graphics.Measurestring to determine the length in pixels without actually drawing it. There are some slight imperfections with it, so you may think about adding or removing some padding. After a test or two, you can get pretty close. That's as proper of a way as I know of, and it doesn't involve the text being in a label.
Also, trying to find a way to get the TableLayoutPanel to calculate sizes without displaying it visually just sounds like you're trying to hack it into doing something it wasn't designed to.
It turned out the width returned by GetPreferredSize() was useful, it was just 'too late'; I was working out the correct size and returning it within code that was called from the TableLayoutPanels' OnLayout() method, and setting the column width there has no effect until the next layout.
I had a solution that used a separate Component implementing IExtenderProvider which could be used to join tables together, but due to the issue above it always lagged behind control changes. Even hooking SizeChanged on all the child controls, the TableLayoutPanel gets the event first, and starts layout.
So I couldn't see any other way but to override the layout process itself. I tried creating a LayoutEngine that performed the necessary calculations, resized the columns, then delegated the actual layout work to the old layout engine, but unfortunately Control.LayoutEngine is read-only, and TableLayoutPanel doesn't even use a backing field, it just returns another object directly, so I couldn't even get around it by using reflection to assign the private backing field.
In the end I had to resort to subclassing the control, to override OnLayout(). Here is the result:
public class SynchronizedTableLayoutPanel : TableLayoutPanel
{
/// <summary>
/// Specifies a key used to group <see cref="SynchronizedTableLayoutPanel"/>s together.
/// </summary>
public String SynchronizationKey
{
get { return _SynchronizationKey; }
set
{
if (!String.IsNullOrEmpty(_SynchronizationKey))
RemoveSyncTarget(this);
_SynchronizationKey = value;
if (!String.IsNullOrEmpty(value))
AddSyncTarget(this);
}
} private String _SynchronizationKey;
#region OnLayout(), Recalculate()
protected override void OnLayout(LayoutEventArgs levent)
{
if (ColumnCount > 0 && !String.IsNullOrEmpty(SynchronizationKey))
{
Recalculate();
ColumnStyles[0] = new ColumnStyle(SizeType.Absolute, GetMaxWidth(SynchronizationKey));
}
base.OnLayout(levent);
}
public void Recalculate()
{
var LargestWidth = Enumerable.Range(0, RowCount)
.Select(i => GetControlFromPosition(0, i))
.Where(c => c != null)
.Select(c => (Int32?)((c.AutoSize ? c.GetPreferredSize(new Size(Width, 0)).Width : c.Width)+ c.Margin.Horizontal))
.Max();
SetMaxWidth(this, LargestWidth.GetValueOrDefault(0));
}
#endregion
#region (Static) Data, cctor, AddSyncTarget(), RemoveSyncTarget(), SetMaxWidth(), GetMaxWidth()
private static readonly Dictionary<SynchronizedTableLayoutPanel, Int32> Data;
static SynchronizedTableLayoutPanel()
{
Data = new Dictionary<SynchronizedTableLayoutPanel, Int32>();
}
private static void AddSyncTarget(SynchronizedTableLayoutPanel table)
{
Data.Add(table, 0);
}
private static void RemoveSyncTarget(SynchronizedTableLayoutPanel table)
{
Data.Remove(table);
}
private static void SetMaxWidth(SynchronizedTableLayoutPanel table, Int32 width)
{
Data[table] = width;
foreach (var pair in Data.ToArray())
if (pair.Key.SynchronizationKey == table.SynchronizationKey && pair.Value != width)
pair.Key.PerformLayout();
}
private static Int32 GetMaxWidth(String key)
{
var MaxWidth = Data
.Where(p => p.Key.SynchronizationKey == key)
.Max(p => (Int32?) p.Value);
return MaxWidth.GetValueOrDefault(0);
}
#endregion
}
This version only cares about the first column, but it could be adapted to synchronise other columns, or rows.
This approach does not flicker or cause jumps with sizing:
public partial class Form1 : Form
{
private readonly Timer _timer = new Timer();
public Form1()
{
InitializeComponent();
_timer.Interval = 500;
_timer.Tick += (o, ea) => UpdateWithRandomSizes();
_timer.Start();
}
private void UpdateWithRandomSizes()
{
var rand = new Random();
label1.Text = new string('A', rand.Next(10));
label2.Text = new string('B', rand.Next(10));
label3.Text = new string('C', rand.Next(10));
label4.Text = new string('D', rand.Next(10));
tableLayoutPanel1.ColumnStyles[0].SizeType = SizeType.AutoSize;
tableLayoutPanel2.ColumnStyles[0].SizeType = SizeType.AutoSize;
var width1 = tableLayoutPanel1.GetColumnWidths()[0];
var width2 = tableLayoutPanel2.GetColumnWidths()[0];
var max = Math.Max(width1, width2);
tableLayoutPanel1.ColumnStyles[0].Width = max;
tableLayoutPanel1.ColumnStyles[0].SizeType = SizeType.Absolute;
tableLayoutPanel2.ColumnStyles[0].Width = max;
tableLayoutPanel2.ColumnStyles[0].SizeType = SizeType.Absolute;
}
}

Categories

Resources