RadSpreadsheet - Give columns a unique identifier - c#

I've some issues giving the columns a unique identifier using Teleriks RadSpreadsheet (Workbook, Worksheet etc).
Here is my problem: My first row is a header row, e.g.:
| Name (father) | Age | Name (mother) | ....
Those column headers in my first row aren't unique. They have been named exactly the same, although they are used for different things. To improve extendibility, I dont want to use indexes to find the specified column I am looking for. When I add another column inbetween, the program should still work fine without any adjustments.
Instead, my program should use a normal key to find the column. For example: My program gets an request to return the column, in which
What I've tried:
- Hidden columns are not available in the version I currently use.
- Finding the columns by an easy string search isnt working either, cause they arent unique, so if I search for the father name column, I have two columns with the header "name" and cannot differentiate, what exactly is the right column.
for (int i = 0; i < columnCount; i++)
{
//Check value equals searchtxt..
}
- I wasnt been able (havent found anything) to give the cells / columns a hidden name, like 'mother name' and 'father name')
Do you have any other idea?

You could use a data structure like Dictionary for example where you can define a key - which will be the "hidden name", and a value - the CellIndex of the header cell.
To keep the dictionary up-to-date you will have to subscribe to some events like
Worksheet.Cells.CellRangeInsertedOrRemoved
and update the cached names to match their eventually new CellIndex. This way you will use the dictionary to find the cell index corresponding to the name defined by you.

Related

How to exclude first row from sorting out Excel column by using PowerShell

I want to sort out one column (it is a target date and I want to sort it out by earliest date).
The thing is that the first row is a text (name of the column), so when I sort it out by column F (Target Date), text row goes to the very end of the excel document.
I would like for this column names to stay and be on top of every column.
Column I want to sort out
Here is the code:
$objRange = $worksheet.UsedRange
$objRange2 = $Excel.Range("F2")
[void]$objRange.Sort($objRange2.Range("F2"))
I am not sure how to exclude from sorting this first row.
Since your range has headers, you have to add an argument to the Sort method to indicate it should not sort the headers.
Your range command would then look like this:
[void]$objRange.Sort($objRange2,1,$null,$null,1,$null,1,1)
The Sort method doesn't like $null for the xlSortOrder parameters so I just specified 1 which means ascending. If you need descending, use 2. The last 1 is the parameter we really care about. Specifying 1 means yes, the range has headers that should not be included in the sort operation.
Syntax reference:
https://msdn.microsoft.com/en-us/library/office/ff840646.aspx

Using Excel interop to retrieve information from the range that meet criteria

oSheet = (Excel._Worksheet)xlWorkBook.ActiveSheet;
oRng = oSheet.get_Range("T10", "T343");
The range oRng contains values of type double. Each cell in Column T shows the max number of the associated row. How can i find out how many 1’s , 2’s 3’s ….. till 10 are in that range. Secondly Eg if there are 20 rows with value =3 , I need to copy column A,B,C from those rows and store them for later use .i need the count of the number of rows for each value from 1 to 10
Here are a few general observations that might be enough to get you going:
Excel.Range has an AutoFilter method that you might be able to employ successively for each value that you're interested in (i.e, 1 through 10). Once you have the individual ranges returned by AutoFilter, you can them query them for the specific information you're interested in. See C# Excel Automation: Retrieving rows after AutoFilter() with SpecialCells() does not seem to work properly for issues associated with this approach.
Alternatively, you might be able to do something like create a simple dictionary that you could then populate as you iterate over column T. For example, the dictionary could be of type Dictionary>.
As you proceed through column T, you encounter a value in each cell. If the cell value hasn't been seen before, you add it as a new key to the Dictionary. For the associated value in the dictionary's key/value pair, you create a new List with the corresponding row number as its first element.
If the cell value has been seen before, you look it up in the dictionary, then add the corresponding row to the List associated with that key.
At the end of the day, your dictionary's keys contains all the values found in column T. The number of rows associated with each value is just the number of elements in the associated List. With the row values in the List, you can then find "A[row value]", "B[row value]" and "C[row value]".

datagridview column index

I have a form with a DataGridView widget and I need to get the index of the column with the selected name.
For example, let's say that I have a table with 2 columns: Name, Surname. I need a way to get index of the column name. The problem is that it changes all the time depending on the DataSource but that column always has the same name "Name".
Does anyone know how to solve the problem?
To retrieve a DataGridView column by name you simply reference it through the columns collection indexer:
datagridview1.Columns["columnName"]
Then you can get the column index from that column:
datagridview1.Columns["columnName"].Index;
Do note that if you use an invalid column name then this reference will return null, so you may want to check that the column reference is not null before using it, or use the columns collection .Contains() method first.
If I am right, e.ColumnIndex will also work for this. you can check the MSDN Documentation here
You can get the index by using the Index property of the DataGridViewColumn widget, as such:
ColumnName.Index
This avoids the need for checking whether the column name is valid at runtime as it will generate a compilation error if the column does not exist. This also makes refactoring easier.
I recommend you give the columns a sensible name (for example DCOL_SomeName) so that you can easily distinguish them. Including the name of the DataGridView widget would help if you have multiple DataGridView widgets on the same form.
create a static class below the code
public static class MyTools
{
public static int IndexByName(this DataGridView dgv, string name)
{
foreach(DataGridViewColumn col in dgv.Columns)
{
if(col.HeaderText.ToUpper().Trim() == name.ToUpper().Trim())
{
return col.Index;
}
}
return -1;
}
}
and then call it with your dataGridView
int index = datagridview1.IndexByName("columnName");
I have found it safer to use the column object's Name property, instead of using the column name as a string, because this allows for more consistent code refactoring in the future.
datagridview1.Columns[column1.Name].Index;
Also, it is important to first make sure the column is not null and, as others have said, that it is contained within the datagridview.

How to create LookUp fields in DataGridView?

In my DataGridView I'am displaying a buch of columns from one table. In this table I have a column which points to item in another table. As you may already guessed, I want to display in the grid in one column some text value from the second table instead of and ItemID.
I could not find a right example on the net how to do this.
Lets assume that I have two tables in databes:
Table Users:
UserID UserName UserWorkplaceID
1 Martin 1
2 John 1
3 Susannah 2
4 Jack 3
Table Workplaces:
WorkplaceID WorkplaceName
1 "Factory"
2 "Grocery"
3 "Airport"
I have one untyped dataset dsUsers, one binding source bsUsers, and two DataAdapters for filling dataset (daUsers, daWorkplaces).
Code which I am performing:
daUsers.Fill(dsUsers);
daWorkplaces.Fill(dsUsers);
bsUsers.DataSource = dsUsers.Tables[0];
dgvUsers.DataSource = bsUsers;
At this point I see in my dgvUsers three columns, UserID, UserName and UserWorkplaceID. However, instead of UserWorkplaceID and values 1,2,3 I would like to see "Factory", "Grocery" and so on...
So I've added another column to dgvUsers called "WorkplaceName" and in my code I am trying to bind it to the newly created relation:
dsUsers.Relations.Add("UsersWorkplaces", dsUsers.Tables[1].Columns["WorkplaceID"], dsUsers.Tables[0].Columns["UserWorkplaceID"]);
WorkplaceName.DataPropertyName = "UsersWorkplaces.WorkplaceName";
Unfortunately that doesn't work. Relation is created without errors but fields in this column are empty after running the program.
What I am doing wrong?
I would like to also ask about an example with LookUp combobox in DataGridView which allow me to change the UserWorkplaceID but instead of numeric value it will show a tex value which is under WorkplaceName.
Thanks for your time.
In my opinion, the best decision would be to use the DataGridViewComboBoxColumn column type. If you do it, you should create a data adapter with lookup data beforehand and then set DataSource, DataPropertyName, DisplayMember, and ValueMember properties of the DataGridViewComboBoxColumn. You could also set the DisplayStyle property to Nothing to make the column look like a common data column. That's it.
I don't know if you can do exactly what you want, which seems to be binding the DataGridView to two different DataTable instances simulataneously. I don't think the DataGridView class supports that -- or if it does it's a ninja-style move I haven't seen.
Per MSDN, your best bet is probably using the CellFormatting event on the DataGridView and check for when the cell being formatted is in the lookup column, then you could substitute your value from the other table. Use an unbound column for the WorkplaceName column, hide the UserWorkplaceID column and then implement the CellFormatting event handle to look up the value in the row, e.g.:
private void dgv_CellFormatting(object sender,
DataGridViewCellFormattingEventArgs e)
{
if (dgv.Columns[e.ColumnIndex].Name.Equals("WorkplaceName")
{
// Use helper method to get the string from lookup table
e.Value = GetWorkplaceNameLookupValue(
dataGridViewScanDetails.Rows[e.RowIndex].Cells["UserWorkplaceID"].Value);
}
}
If you've got a lot of rows visible, this might impact performance but is probably a decent way to get it working.
If this doesn't appeal to you, maybe use the DataTable.Merge() method to merge your lookup table into your main table. A quick glance at one of my ADO.NET books suggests this should work, although I have not tried it. But I'm not sure if this is too close to the idea suggested previously which you shot down.
As for your second question about the lookup combobox, you should really post it in a separate question so it gets proper attention.
You could make SQL do the job instead. Use a join to return a table with Workplace names instead of IDs, output that table into a dataset and use it instead.
eg.
SELECT A.UserID, A.UserName, B.WorkplaceID
FROM Users A
JOIN Workplaces B ON A.UserWorkplaceID = B.WorkplaceID
Then use its output to fill dsUsers.

How to access Parents Child in DataColumn Expression

I've a DataSet with 3 DataTables:
dtPerson
dtSalary
dtFriend
Every person has salaries, and every person has one friend.
I've added a column dcHisFriend into dtSalary and would like to display friend name of a person owning specified salary.
So dtPerson has a column NAME, dtSalary has column VALUE and dtFriend has a column NAME.
I've added column dcHisFriend and set Expression to this:
dtSalary.Add(dcHisFriend);
dcHisFriend.Expression =
"Max(Parent.Child(Persons_Friend).NAME)";
But this obviously does not work.
Could you please tell me how to
put into column dcHisFriend name of a friend of a person with a salary into salary table?
I think, there is no way how to access any other row in "Expression" in DataColumn.
The only way, how to achieve similar behaviour is hook to DataColumnChanged event on DataTables where are source data and then set the computed value to the regular column (=column without expression).
There is actually a way to do this, provided the relationships between your tables are 1 to 1 (though missing rows aren't a huge problem): Create two relations rather than one, i.e.
var joinColT1 = table1.Columns["ID"];
var joinColT2 = table2.Columns["FK_IDT1"];
var rel1 = new DataRelation("R1To2", joinColT1, joinColT2, false);
var rel2 = new DataRelation("R2To1", joinColT2, joinColT1, false);
theDataSet.Relations.Add(rel1);
theDataSet.Relations.Add(rel2);
// Add the column you're after
var hisFriend = new DataColumn("HisFriend", typeof(string), "Parent([R2To1]).[HisFriend]");
table1.Columns.Add(hisFriend);
// Add a back-reference to the other table against the friend if you want, too
var hisFriendsSalary = new DataColumn("HisFriendsSalary", typeof(decimal) "Parent([R1To2]).[Salary]");
table2.Columns.Add(hisFriendsSalary);
A couple of notes, though: first, when I was first experimenting with this, I got syntax errors without the square brackets around the relation names in the expression. That might just have been to do with the names I'd used for the relations though.
Secondly, I believe the result of Expressions are stored against the rows (they aren't computed "just in time" on access, they're computed when values change, and the results are kept). That means you are storing the data twice by using this approach. Sometimes that's fine, and sometimes it isn't.
Third, you'll note that I'm not using constraints. That's because in my typical use-cases, I'm not expecting every row to have an analogue in the other table (that's why there are two tables in the first place, quite often!). That may (I haven't checked dotnetframework.org) have an impact on performance.

Categories