How to get the column counts of StackedHeaders? - c#

In my project, I have checked with many test cases and in one test case, I have dragged the stacked column header, I need to get the column counts belongs to that StackedHeader because I have to do some customization with their column index. Please anyone suggest me how to achieve this.
Thanks!

Define your code like:
private int RecursiveIteration(GridFieldCollection field) {
int count = 0;
for (int i = 0; i < field.Count ; i++)
{
if (field[i].GetType() == typeof(GroupField))
{
count += RecursiveIteration((field[i] as GroupField).Columns);
}
count++;
}
return count; }
To get total count: int colCount = RecursiveIteration(WebDataGrid1.Columns);

Related

Use Last Value in Column for Summary Instead of SUM

I have following code to remove the summary row/band value on a retrieve UI event. I'm certain this is the wrong way to do it but it works.
public UiEventResult AfterRetrieveData_111(object sender, RetrieveDataEventArgs e)
{
UltraGridBase baseGrid = _view.ViewGrids["ultraGrid1"];
UltraGridLayout gridLayout = baseGrid.DisplayLayout;
for (int i = 0; i < 2; i++)
{
gridLayout.Bands[i].Columns["columnA"].Formula = "''";
}
for (int i = 0; i < 3; i++)
{
gridLayout.Bands[i].Columns["columnB"].Formula = "''";
gridLayout.Bands[i].Columns["columnC"].Formula = "''";
}
Is there a way to program the retrieve so that it populates the summary row for column A/band[2] so that is uses the last value in each column? Without the above code it will sum rows under but would like for a way for it to use the last row value instead. Data will always be sorted DESC by date so last row will always be the value needed...
One way to achieve this is in InitializeRowEvent by setting the value of the columnA to the value of the last row in the child band like this:
// Update the rows only in the first band. You can also use e.Row.Band.Key == {YOU_BAND_KEY}
if (e.Row.Band.Index == 0)
{
// set the value of the columnA to the value of the last row in the child band
e.Row.Cells["columnA"].Value = e.Row.ChildBands.LastRow.Cells["columnA"].Value;
}
Note, this will not work if you edit the cells values. If you need to update the parent row value after cell update, again in InitializeRowEvent you can add this:
// look for row in the secon band
if (e.Row.Band.Index == 1)
{
// get the last row in the second band
if (e.Row == e.Row.ParentRow.ChildBands.LastRow)
{
// get the value of the last row in the second band and set it to the parent row
e.Row.ParentRow.Cells["columnA"].Value = e.Row.Cells["columnA"].Value;
}
}
This will loop through ChildBands and set parent row value with the last value in each ChildBand.
int rowCount = gridLayout.Rows.Count;
for (int i = 0; i < rowCount; i++)
{
foreach (UltraGridChildBand childBand in baseGrid.Rows[i].ChildBands)
{
foreach (UltraGridRow row in childBand.Rows)
{
row.Cells["columnA"].Value =row.ChildBands.LastRow.Cells["columnA"].Value;
}
}
}

Merge values on multi-line columns on a Datagridview

I have Visual Studio 2019. The project is a .Net Windows Form on C# on .Net Framework 4.8.
I have a Datagridview which shows some tables data from different databases (MS SQL and Postgresql).
I merge that databases, and the result is too long, so we can't fit it on a screen; but we have to see all the data available on that screen, which are more than 40 columns. Reduce the font size is not plausible.
So, the solution proposed was to merge some values on the same column in this way (See this example):
The actual data view:
The way we need to view it:
If you have any ideas or you know an alternative to Datagridview which allows that, please share them.
Thanks in advance.
You can customize your datagridview's row and column to get the Multi-line columns on a datagridview.
I assume that the datatable is the table from database.
using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
private void Form1_Load(object sender, EventArgs e)
{
DataTable table = new DataTable();
table.Columns.Add("Name");
table.Columns.Add("Field1");
table.Columns.Add("Field2");
table.Columns.Add("Field3");
table.Columns.Add("Field4");
table.Columns.Add("Field5");
table.Columns.Add("Field6");
table.Columns.Add("Field7");
table.Rows.Add("test1", 1, 2, 3, 4, 5, 6, 7);
table.Rows.Add("test2", 1, 2, 3, 4, 5, 6, 7);
table.Rows.Add("test3", 1, 2, 3, 4, 5, 6, 7);
dataGridView1.ColumnHeadersVisible = false;
for (int i = 0; i < table.Columns.Count/2; i++)
{
dataGridView1.Columns.Add("","");
}
string[] columnNames = table.Columns.Cast<DataColumn>()
.Select(x => x.ColumnName)
.ToArray();
int count = table.Columns.Count/2;
var col1 = columnNames.Take(count).ToArray();
var col2= columnNames.Skip(count).Take(count).ToArray();
dataGridView1.Rows.Add(col1);
dataGridView1.Rows.Add(col2);
object[] arr;
for (int i = 0; i < table.Rows.Count; i++)
{
arr = table.Rows[i].ItemArray;
var row1=arr.Take(count).ToArray();
var row2 = arr.Skip(count).Take(count).ToArray();
dataGridView1.Rows.Add(row1);
dataGridView1.Rows.Add(row2);
}
}
Result:
After reviewing your question, I have to say that IMHO your solution is not giving much thought to the end user or if the code has to grab one of the values. Stacking fields into a single column “creates” two issues IMHO…One, as mentioned, is that the user is going to have to do extra work and check the order of the headers to distinguish which field value is which… a subtle yet (annoying) non intuitive extra step. Two, if the user is allowed to change fields or the code needs to grad a field, then, there is going to be extra work needed to differentiated which field goes with which value. Extra work for the user and extra work for the coder doesn’t sound like a good start.
Sorry about my rant. Fortunately, if you wanted to take a table as shown in the question, and turn it into a table as you describe, then the code below should do this. It basically creates “two” (2) field columns. Such that each column contains two fields. The code is hacky yet it is not too complicated I hope. I made numerous comments in the code. Some notes would be that, since we are adding two fields for each column and (as far as I know) a DataGridView won’t allow double column headers, the code does NOT use the column headers row and instead uses the first two rows of the grid for the two column headers. This will allow you to format the two rows to look like headers and/or color code if needed.
Lastly, a better solution IMHO. As previously mentioned a pivot will work, however, there are a couple of issues given how the data is stored in the original table. In a basic pivot where we switch rows and columns, the posted example would have three (3) columns… “Jim”, “Hugh” and “Terrance”. Then the number of rows would be (one (1) + however many fields/field columns). The extra “one” is the field “LastName.” Given this, it may look something like…
Jim Hugh Terrance
LastName Carey Jackman Hill
Field1 1 a N/A
Field2 2 b N/A
……..
It would appear obvious that the “LastName” should go with the column header. Therefore, the transpose/pivot may look like…
Jim Carey Hugh Jackman Terrance Hill
Field1 1 a N/A
Field2 2 b N/A
……..
IMHO, this will be more intuitive for the user to identify fields and there should not be any extra coding if we need to reference a specific value. The picture below shows a complete example from the code below. Drop three (3) DataGridViews onto a form and paste my code. The top-left grid is the original data. The bottom-left grid is the transpose as per your requirements and finally, the grid on the right is the what I feel would work best considering your dilemma.
A note on the last grid on the right… Initially, like the column header rows in your solution, I had the fields as a column in the grid. It will not be difficult to change the code if you want this. However, the code currently adds the field names as “row headers” in the grid. Since the DataTable does not really have row headers, this addition had to be made “after” the data source was set and can be seen in the forms Load event. Again, it will not be difficult to move the field to an added column in the DataTable.
To make this example complete, below is code to create some test data. The incoming parameter totalCols will make totalCols columns in the DataTable for the "Field" values.
private DataTable GetDataFromDB(int totalCols) {
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("LastName", typeof(string));
for (int i = 1; i <= totalCols; i++) {
dt.Columns.Add("Field" + i, typeof(string));
}
DataRow curRow;
string name;
string lName;
for (int i = 1; i < 4; i++) {
switch (i) {
case 1:
name = "Jim";
lName = "Carrey";
break;
case 2:
name = "Hugh";
lName = "Jackman";
break;
default:
name = "Terence";
lName = "Hill";
break;
}
curRow = dt.NewRow();
curRow["Name"] = name;
curRow["LastName"] = lName;
if (i < 3) {
for (int j = 2; j < dt.Columns.Count; j++) {
curRow[j] = "N" + i + "F" + (j - 1);
}
}
dt.Rows.Add(curRow);
}
return dt;
}
We will use three (3) global DataTables, one for each grid.
DataTable originalDT;
DataTable pivotDT1;
DataTable pivotDT2;
The load event that sets each grid to the proper DataTable and some specific formatting for each grid.
private void Form1_Load(object sender, EventArgs e) {
originalDT = GetDataFromDB(45);
dataGridView1.DataSource = originalDT;
// pivot 1 - bottom left grid
pivotDT1 = PivotTable(originalDT);
dataGridView2.DataSource = pivotDT1;
dataGridView2.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
dataGridView2.Rows[0].DefaultCellStyle.BackColor = Color.Blue;
dataGridView2.Rows[1].DefaultCellStyle.BackColor = Color.Blue;
dataGridView2.Rows[0].DefaultCellStyle.ForeColor = Color.White;
dataGridView2.Rows[1].DefaultCellStyle.ForeColor = Color.White;
dataGridView2.Columns[0].Frozen = true;
// pivot 2 - right grid
pivotDT2 = PivotTable2(originalDT);
dataGridView3.DataSource = pivotDT2;
int dgvRow = 0;
// add column headers as row headers in the grid
for (int i = 2; i < originalDT.Columns.Count; i++) {
dataGridView3.Rows[dgvRow++].HeaderCell.Value = originalDT.Columns[i].ColumnName;
}
dataGridView3.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders;
}
Finally, the two pivot/transform methods…
Using your solution and shown in the bottom-left grid…
private DataTable PivotTable(DataTable originalDT) {
DataTable pivotDT = new DataTable();
// the number of columns will be half the original number of columns
int halfCols = Math.DivRem(originalDT.Columns.Count, 2, out int rem);
// if there is a remainder then there is an odd number of columns and we need to add 1 col
if (rem > 0) {
halfCols++;
}
// add the columns to the pivot table
for (int i = 0; i < halfCols; i++) {
pivotDT.Columns.Add();
}
// the number of rows will be the number of original rows times 2
// PLUS 2 additional rows for the headers
for (int i = 0; i < (originalDT.Rows.Count * 2) + 2; i++) {
pivotDT.Rows.Add();
}
// Add the two header rows from the column names
int originalCol = 0;
for (int i = 0; i < halfCols; i++) {
pivotDT.Rows[0][i] = originalDT.Columns[originalCol++].ColumnName;
// if the original table had an odd number of columns
// then the last column only had one field
// - there would never be a column without at least one field
if (originalCol < originalDT.Columns.Count) {
pivotDT.Rows[1][i] = originalDT.Columns[originalCol++].ColumnName;
}
}
// finally add the rows from the original table.
int pivotRow = 2;
int pivotCol = 0;
int curPivotRow;
int curPivotCol;
string value;
for (int originalRow = 0; originalRow < originalDT.Rows.Count; originalRow++) {
curPivotRow = pivotRow;
curPivotCol = pivotCol;
for (originalCol = 0; originalCol < originalDT.Columns.Count; originalCol++) {
value = originalDT.Rows[originalRow][originalCol].ToString();
if (string.IsNullOrEmpty(value)) {
value = "N/A";
}
pivotDT.Rows[curPivotRow][curPivotCol] = value;
// if this is the first item then simply bump the pivot row
if (curPivotRow < pivotRow + 1) {
curPivotRow++;
}
else { // this is the second item -
// we want the curpivot row to start back at the starting pivotRow
// then move over a column for the next two columns in the original table
curPivotRow = pivotRow;
curPivotCol++;
}
}
// new row in the original data start back at column 0 in the pivot table
// and bump the row index by two since we added two rows
pivotRow += 2;
pivotCol = 0;
}
return pivotDT;
}
And my solution shown in the grid on the right.
private DataTable PivotTable2(DataTable originalDT) {
DataTable pivotDT = new DataTable();
for (int i = 0; i < originalDT.Rows.Count; i++) {
pivotDT.Columns.Add();
}
for (int i = 0; i < originalDT.Columns.Count - 2; i++) {
pivotDT.Rows.Add();
}
int pivotCol = 0;
foreach (DataRow row in originalDT.Rows) {
pivotDT.Columns[pivotCol++].ColumnName = row[0].ToString() + " " + row[1].ToString();
}
int pivotRow = 0;
pivotCol = 0;
string value;
for (int i = 0; i < originalDT.Rows.Count; i++) {
for (int j = 2; j < originalDT.Columns.Count; j++) {
value = originalDT.Rows[i][j].ToString();
if (string.IsNullOrEmpty(value)) {
value = "N/A";
}
pivotDT.Rows[pivotRow++][pivotCol] = value;
}
pivotCol++;
pivotRow = 0;
}
return pivotDT;
}
Finally, I am not that proficient using SQL, however, I am betting it is possible to create an SQL procedure that will produce my solution directly from the data base.

How to calculate the total price from one data grid view and update it then show it in another dataGridViwe C#

what am doing is a pizza menu, and I have 3 dataGridView, 1st to show the menu, 2nd to show the ingredients from the selected pizza, and 3rd to show a list of ingredients (the user can add remove ingredient). The thing is I need to calculate the price of the ingredient from the 2nd DGV by updating it automatically and then show the final price in 1st DGV
pizzaGrid.DataSource = SelectedList;
DataGridViewCell pizzacell = MenuGrid.CurrentCell;
DataGridViewCell ingrecell = IngredientCell.Currentcell;
int total = 0;
for(int i = 0; i < ingredientList.Count; i++)
{
Ingredient ingre = ingredientList[i];
if(ingredientList.Contain(i))
{
total++;
SelectedList.Add(i);
}
}
if i understands your question correct you want to calculate the total price of all the values in a column from a datagrid you can try some thing like this
int sum = 0;
for (int i = 0; i < dataGridView1.Rows.Count; ++i)
{
sum += Convert.ToInt32(dataGridView1.Rows[i].Cells[1].Value);
}
label1.text = sum.ToString();
You should build a method that looks something like this:
private double calculatePrice()
{
double sum = 0; //Double, because we are dealing with money, which is a decimal
for(int i = 0; i < IngredientGrid.Rows.Count; i++)
{
sum += Convert.ToDouble(IngredientGrid.Rows[i].Cells[1].Value); //This takes the value of your selected cell within your ingredients datagrid (they need to be only numbers, no letters since we're converting to a double)
}
return sum;
}
And you then call this method on for instance an index changed or whenever like this:
label1.Text = calculatePrice().ToString();

Loop through selected rows C# DevExpress

I have a function that sets column values for all rows:
The code that sets this:
//Update the engineers for all rows
Btn_ValidateClick_ItemClick(object sender,ItemClickEventArgs e)
{
UpdateTotalTime(gridView);
}
private void UpdateEngineers(DevExpress.XtraGrid.Views.Base.ColumnView View)
{
//Column name that need to be updated (set)
DevExpress.XtraGrid.Columns.GridColumn col = View.Columns.ColumnByFieldName("Engineers");
try
{
int dataRowCount = View.DataRowCount;
for (int i = 0; i < dataRowCount; i++)
{
GridView detail = (GridView)gridView.GetDetailView(i, 0);
string language = gridView.GetRowCellValue(i, "Language").ToString();
for (int y = 0; y < gridView.GetDetailView(i, 0).RowCount; y++)
{
//Add all values found in a detail column to an arraylist
values.Add(detail.GetRowCellValue(y, "EngineerInitials").ToString());
}
if (values.Count >0 )
object t = //string join ...
View.SetRowCellValue(i, col, t);
}
else
{
object t = "No engineers"
View.SetRowCellValue(i, col, t);
}
}
}
}
}
The problem is that now, I want it only to set it for the rows that are selected.
I tried using the .GetSelectedRows()-function and adding the rows to an ArrayList, but this doesn't allow customability really:
private void UpdateTotalTime(DevExpress.XtraGrid.Views.Base.ColumnView View)
{
ArrayList selectedRows = new ArrayList();
for (int i = 0; i < gridView.SelectedRowsCount; i++)
{
if (gridView.GetSelectedRows()[i] >= 0)
selectedRows.Add(gridView.GetDataRow(gridView.GetSelectedRows()[i]));
}
try
{
int count = View.GetSelectedRows().Count();
for (int i = 0; i < selectedRows.Count; i++)
{
//This gets the first row of the count, not the first selected row
GridView detail = (GridView)gridView.GetDetailView(i,0);
}
}
If I select the 3 bottom rows, the first 3 get updated. Why is this?
You are adding all the selected rows to your selectedRows ArrayList. But after that, you are not using it for anything.
I guess what you want (I've never used devexpress controls) is using those selectedrows RowHandle to pass it to the GetDetailView method. According to the GetSelectedRows documentation, the method returns the int handles of the selected rows, so your code should look like this:
First, you must save the DataRow handles, not the DataRow itself, so you must change in your code this line:
selectedRows.Add(gridView.GetDataRow(gridView.GetSelectedRow‌​s()[i]));
into this:
selectedRows.Add(gridView.GetSelectedRows()[i]);
And then, change your loop into this:
for (int i = 0; i < selectedRows.Count; i++)
{
int rowHandle = (int)selectedRows[i];
GridView detail = (GridView)gridView.GetDetailView(rowHandle,0);
}
In fact, you could do everything in just one loop:
private void UpdateTotalTime(DevExpress.XtraGrid.Views.Base.ColumnView View)
{
for (int i = 0; i < gridView.SelectedRowsCount; i++)
{
int rowHandle = gridView.GetSelectedRows()[i];
GridView detail = (GridView)gridView.GetDetailView(rowHandle,0);
}
}

Null Reference Exception being given when trying to play sound

I am trying to insert a sound whenever there is a match in my game. However, I keep getting a NullReferenceException when I get the match. It goes to the line soundEffectInstance.Play();. Any help is greatly appreciated. I've attempted everything when trying to get it to work. Here is my code:
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
namespace AndyKlax.KlaxGame
{
internal class Grid
{
private const int GridOrigin = 660;
SoundEffect soundEffect;
SoundEffectInstance soundEffectInstance;
private readonly List<Block>[] _grid = new List<Block>[Klax.NumberOfColumns];
public void LoadContent(ContentManager content)
{
soundEffect = content.Load<SoundEffect>(#"Sounds/24372^pop2.mp3");
soundEffectInstance = soundEffect.CreateInstance();
}
internal Grid()
{
for (int i = 0; i < Klax.NumberOfColumns; i++ )
{
_grid[i] = new List<Block>();
}
}
private bool RemoveBlocks()
{
var foundBlocksToRemove = false;
//Mark all blocks in the grid as unmatched
for (int column = 0; column < Klax.NumberOfColumns; column++)
{
for (int row =_grid[column].Count -1 ; row >=0 ; row--) //Loop backwards do its safe to remove
{
if (_grid[column][row].Delete)
{
_grid[column].RemoveAt(row);
foundBlocksToRemove = true;
}
}
}
return foundBlocksToRemove;
}
private void SearchForMatches()
{
//Search the whole grid - look for matches in each direction and set the deleted flag for anything you find
for (int column = 0; column < Klax.NumberOfColumns; column++)
{
for (int row = 0; row < _grid[column].Count; row++)
{
SearchForMatches(column, row, 1, 0); //Horizontal
SearchForMatches(column, row, 0, 1); //Vertical
SearchForMatches(column, row, 1, 1); //Diagonal one way
SearchForMatches(column, row, 1, -1); //Diagonal the other way
}
}
}
private void SearchForMatches(int column, int row, int xStep, int yStep)
{
int startColorIndex = _grid[column][row].ColorIndex;
int matchCount = 0;
int x = column;
int y = row;
//Loop in the given direction till we run out of color or grid
do
{
matchCount++;
x += xStep;
y += yStep;
} while (x < Klax.NumberOfColumns && y < _grid[x].Count && y>0 && startColorIndex == _grid[x][y].ColorIndex);
//If the match is long enough
if (matchCount >= 3)
{
//Then mark all those blocks for removal
x = column;
y = row;
//Loop in the given direction till we run out of color or grid
do
{
_grid[x][y].Delete = true;
x += xStep;
y += yStep;
soundEffectInstance.Play();
} while (x < Klax.NumberOfColumns && y < _grid[x].Count && startColorIndex == _grid[x][y].ColorIndex);
}
}
private void ResetDeleteFlags()
{
//Mark all blocks in the grid as unmatched
for (int column = 0; column < Klax.NumberOfColumns; column++)
{
for (int row = 0; row < _grid[column].Count; row++)
{
_grid[column][row].Delete = false;
}
}
}
internal void Draw(SpriteBatch spriteBatch)
{
//Like the paddle the position of the block is inferred by its position in the grid
spriteBatch.Begin();
for (int column = 0; column < Klax.NumberOfColumns; column++)
{
for (int row = 0; row < _grid[column].Count; row++)
{
_grid[column][row].Draw(spriteBatch, column, GridOrigin - row * Klax.Texture.Height);
}
}
spriteBatch.End();
}
internal void Add(int column, Block block)
{
_grid[column].Add(block);
//Search for matches
do
{
ResetDeleteFlags();
SearchForMatches(); //Look for matches and mark their deleted flag
} while (RemoveBlocks()); //If we removed any then iterate again as there may be new matches
}
public bool HasSpace(int column)
{
//Is there room in this column
return _grid[column].Count < Klax.NumberOfRows;
}
}
}
From what I'm seeing in your code you are using ContentManager.Load from the Microsoft.Xna.Framework framework.
Under this assumption your code probably won't ever work and always will return null.
The problem here is twofold:
ContentManager.Load supports only specific types as given by its definition (I'll post a link further below). SoundEffects are not part of these types
This load method thinks that all files given have no extension by themselves but instead have an ungiven .xnb extension (and are of the xnb format).
Thus your Sounds/24372^pop2.mp3 translates to Sounds/24372^pop2.mp3.xnb for it.
One example directly from the documentation how it is used:
Model model = contentManager.Load<Model>( ".\\content\\models\\box" );
This would load box.xnb as a model.
The full documentation for that method is here:
https://msdn.microsoft.com/en-us/library/bb197848.aspx
Like I mentioned if my assumption that you are using THAT method is correct then it is clear why the loading fails with that as it is looking for a file that does not exist. But even if that is corrected according to the documentation it should still not work as it is not of a supported type (I find it strange though that the method does not throw an exception when you try to use it to load something not supported but that developer oversight has nothing to do with your problem at all in essence).

Categories

Resources