How to read data from file geodatabase tables dynamically? - c#

foreach(Row in table.Search("*","",rowinstance.recycle()))
{
City current_city = new City();
current_city.cityname = row.GetString("cityname");
PointShapeBuffer geometry = row.GetGeometry();
psri.FileGdb.point point = geometry.point;
current_city.xcord = point.x;
current_city.ycord = point.y;
current_city.popCateogry = row.getInteger("pop");
}
In above code i am retrieving different values from one row like row.GetString(), row.GetInteger() etc, How about if I don't know the data type of that row, how can I retrieve values from row generically?
I have tried var type = row.GetType() but its not working, its giving some other sort of information, not about the datatype.

A row in a geodatabase feature class or table will have multiple associated data types. Using your example code, each row will have a geometry attribute, a population attribute (integer), a city name attribute (string), and so on.
You should instead by finding the data type of each attribute (field) before you try to access data from it, and then have appropriate conditionals to handle that.
IField.Type Property (Esri reference page) may get you started.

Related

EPPLUS using loadfromcollection with merged cells

I have to fill an excel like this
Where I have merged cells on each row.
I'm trying to use loadfromcollection, but it ignores the merged cells and it fills each single row.
Please did you know any quick way to do it?
edit:
I think that one way is to implement a custom loadfromcollection where the steps are:
Iterate collection
For each object, read properties/fields using reflection
Iterate cell on each row
check if range address is in worksheet.mergecells
fill it with property/field value
But in case of complex worksheet (with some loadfromcollection), I think that this approach could be heavy with a big cost.
edit 2:
Add an example:
Code like this
public class MyObj
{
public string first {get; set;}
public string second {get; set;}
}
...
List<MyObj> myList = new List<MyObj>() {
new MyObj() {first = "first1", second = "second1"},
new MyObj() {first = "first2", second = "second2"} };
...
ws.Cells["A1"].LoadFromCollection(myList);
In a normal worksheet, loadfromcollection has the following output:
If I merge column A and B after filled, I get this:
In case that my worksheet template is
and then I try to fill it using loadfromcollection I get this result
loadfromcollection ignores merged cell, and fill column A and B, but my expectation is
The problem is that this is an ´intended´ behavior in excel. When you have values in Columns A and B and you merge them, only the value in column A will remain. LoadFromCollection does not have an overload for this kind of scenario.
I can think of several ´workarounds´ which could work, depending on how general you want the code to be.
Your solution of iterating through object properties with reflection would work. If the ´merging´ is always two columns, I´d recommend using a column counter and add 2 to it each property iteration.
So something like this (where of course you already defined the excel package and worksheet etc and the start row and column):
var type = testObject.GetType();
var properties = type.GetProperties();
foreach (var property in properties)
{
cell = worksheet.Cells[row, column];
cell.Value = property.GetValue(testObject);
column += 2;
}
Alternatively, you could decide to remove the merging from the template and move it to your code instead. This may provide a little bit more consistency and flexibility as well.
Code above would then look more like:
var type = testObject.GetType();
var properties = type.GetProperties();
foreach (var property in properties)
{
cellRange = worksheet.Cells[row, column, row, column + 1];
cellRange.Merge = true;
cellRange.Value = property.GetValue(testObject);
column += 2;
}
You could even go a bit 'crazy' and make the range variable based on the property. You make a switch statement with all known properties which should be 2 merged cells, those which should be 3 merged cells etc. Then make the default 1 cell/column. This way you gain total control over the output and it's much easier to adjust in the future.
Edit
My case is a little bit more complex, not all my mergedcells are composed by 2 cells
Some code to illustrate my above statement with some code to help address this. Used the old switch notation as you said it was an 'old project' and I assume you aren't using C# 8 or higher yet.
var type = testObject.GetType();
var properties = type.GetProperties();
int columnMergeSize;
foreach (var property in properties)
{
columnMergeSize = GetPropertyColumnMergeSize(property.Name);
cellRange = worksheet.Cells[row, column, row, column + columnMergeSize];
cellRange.Merge = true;
cellRange.Value = property.GetValue(testObject);
column += 2;
}
private int GetPropertyColumnMergeSize(string propertyName)
{
switch (propertyName)
{
case "Property1":
case "Property2":
case "Property3":
case "Property4":
return 2;
case "Property5":
case "Property6":
return 3;
default:
return 1;
}
}
This will be less heavy than having to read out the properties of the cell each time to see if it's 'merged' or not. I've also found that if you remove nearly all formatting from an excel template it will load much faster speeding up your programme.
You can either decide to put this in a service and just inject the method into wherever it's needed with DI, or you can make it a static method and use it.

DataTable - Columns Name: Automatic changing in two different places

I have two DataTables. I simply wanna save one of them in the other and then change the column name on my "temporary table".
My problem is that everytime that I change the column name from one table, it is automatically changing in the other.
declaration:
public DataTable DataTablefromDataBaseFormDataType { get; private set; }
How do I get value for the first DataTable:
DataTablefromDataBaseFormDataType = ((FormProject)Fproject).DataTablefromDataBaseFormProject;
How do I copy and change column name:
DataTableUsedToPlotGraphTemp = DataTablefromDataBaseFormDataType;
DataTableUsedToPlotGraphTemp.Columns[column_to_use].ColumnName = "temp";
Rest of the code (if needed)
DataTableUsedToPlotGraphTemp = DataTablefromDataBaseFormDataType;
DataTableUsedToPlotGraphTemp.Columns[column_to_use].ColumnName = "temp";
// This point! - When I change DataTableUsedToPlotGraphTemp it is also changing //DataTableUsedToPlotGraphTemp
if (ReceiveStartDate == null && ReceiveEndDate == null)
{
ReceiveStartDate = DataTableUsedToPlotGraphTemp.Rows[0][4].ToString();
ReceiveEndDate = DataTableUsedToPlotGraphTemp.Rows[DataTableUsedToPlotGraphTemp.Rows.Count-1][4].ToString();
}
if (ReceiveNameFile != "All files")
{
string query = $"TIME_FORMATTED >='{ReceiveStartDate}' AND TIME_FORMATTED <='{ReceiveEndDate}'";
DataRow[] result = DataTableUsedToPlotGraphTemp.Select(query);
foreach (DataRow row in result)
{
//MessageBox.Show(row[0].ToString());
DataTableUsedToPlotGraph.ImportRow(row);
}
}
else
{
DataRow[] result = DataTablefromDataBaseFormDataType.Select("FILE_NAMES = '"+ ReceiveNameFile + "'");
foreach (DataRow row in result)
{
//MessageBox.Show(row[0].ToString());
DataTableUsedToPlotGraph.ImportRow(row);
}
}
}
I already debugged several times to see if it is happening in some other place of the code. The moment that it changes the column name, it changes for both DataTables.
Can anybody help me?
Thank you.
You don't have two different DataTables(reference type) but only one:
DataTableUsedToPlotGraphTemp = DataTablefromDataBaseFormDataType;
So if you change one you are changing both. Those are just variables that reference the same object. You can use DataTable.Copy:
DataTable DataTableUsedToPlotGraphTemp = DataTablefromDataBaseFormDataType.Copy();
If you just need an empty table with the same columns you can use DataTable.Clone.
Maybe this explains it a little bit better:
For a value type, the value is the information itself. For a reference
type, the value is a reference which may be null or may be a way of
navigating to an object containing the information.
For example, think of a variable as like a piece of paper. It could
have the value "5" or "false" written on it, but it couldn't have my
house... it would have to have directions to my house. Those
directions are the equivalent of a reference. In particular, two
people could have different pieces of paper containing the same
directions to my house - and if one person followed those directions
and painted my house red, then the second person would see that change
too. If they both just had separate pictures of my house on the paper,
then one person colouring their paper wouldn't change the other
person's paper at all.

How can I get value of a specific cell in DataTable in c#?

I am new to c# and using windows forms.
I have a dataTable as shown in screenshot, I want to get a value of specific cell in button_Text column based on Button_Name column value and save it in a string. I know I can use this code :
string s = DataTable.Rows[2][1];
But I do not want to use it because I do not want to use numbers between brackets to index the cell value but instead I want to use the a Button_Name column value and button_Text column name to index the cell value.
For example:
string s = DataTable.Rows[ButtonUC1_3][Button_Text]; // but it gives error : "Argument1: can not convert from string to int"
I hope it is clear enough. Anyone knows how to achieve this? Thank you
Use LINQ to DataSet there you can use column names
string value = dataTable.AsEnumerable()
.Where((row) => row.Field<string>("button_Name").Equals("ButtonUC_1"))
.Select((row) => row.Field<string>("button_Text"))
.FirstOrDefault();
Here Rows Must be Int formet,But Columns string allowed,
below one is error
string s = DataTable.Rows["ButtonUC1_3"]["Button_Text"];
the correct formet is,
string s = DataTable.Rows[2]["Button_Text"];
It will give the cell value,.
DataTable Rows don't have names, they're indexed only by number. If you want to access the rows through the names you specified, you need to use your own mapping, e.g. var rowMapping = new Dictionary<string,int>() { { ButtonUC1_3, 0}, ... } and then access the DataTable like this DataTable.Rows[rowMapping[ButtonUC1_3]][Button_Text].
Obviously, this is not the only way how to do it, you could define an enum, integer constants, etc. It all depends how many rows you have and how you use your DataTable.

Grid column contains int64 values but filter shows strings and doesn't work/

We've got problem with filtering for some of our columns in devexpress gridcontrol. We add the column dynamically (bound-type column) to the grid. The values from the source objects are long type. In the cells it seems that values are fine (since they're aligned to the right without any custom formating on our side) however in filter popup values behave like strings.
For example data set like 1,2,5,11,22,37 the filter list is sorted like 1,11,2,22,5,37 (just like strings) and when we choose one of the available values the filtering does not work (i mean, grid becames empty). Even filters like "Non empty cells" does not work, but when we choose "empty cells" only few from few thousand rows are shown even if most of the cells have no values.
It is important to point out that only dynamically added columns behave that way, the few columns we create every time our module runs work as intended.
The data source is a container (List like).
We're using DevExpress 13.2.
Example of creating 'custom column'
void CreateColumn(GridColumn gridColumn, string fieldName = null, string caption = null, bool visible = true,
bool readOnly = true, UnboundColumnType unboundType = UnboundColumnType.Bound,
int columnWidth = int.MinValue, int minColumnWidth = int.MinValue)
{
gridColumn.Caption = caption;
if (fieldName != null)
gridColumn.FieldName = fieldName;
gridColumn.Visible = visible;
gridColumn.OptionsColumn.ReadOnly = readOnly;
gridColumn.OptionsColumn.AllowEdit = !readOnly;
gridColumn.UnboundType = unboundType;
gridColumn.OptionsFilter.AllowAutoFilter = true;
gridColumn.FilterMode = ColumnFilterMode.Value;
gridColumn.OptionsFilter.AutoFilterCondition = DevExpress.XtraGrid.Columns.AutoFilterCondition.Contains;
if (columnWidth != int.MinValue)
{
gridColumn.Width = columnWidth;
gridColumn.OptionsColumn.FixedWidth = true;
}
if (minColumnWidth != int.MinValue)
gridColumn.MinWidth = minColumnWidth;
}
GridColumn gridColumn = new GridColumn();
CreateColumn(gridColumn, "someName", "someCaption", true, true);
View.Columns.Add(newGridColumn);
That's how it goes in our code (striped most of not related code just to give example process).
#edit
There's invalid cast exception when we add filter like this:
ColumnFilterInfo filter = GetFilter(); //cant really post code of this
ourGrid.MainView.ActiveFilter.Add(column, filter); // VS points here
Unfortunately it doesnt say what and where (except some 'lambda expression') exception is being thrown.
Of course column is the column mentioned above.
#edit
I've found new "tip". The FilterItem objects contain strings for sure, however they should contain long values. How can we influence the creation of these or atleast where to check why those are created like that (we dont do it manually)?
#Edit 19.11.2015
Alright, I had some breakthrough. The columns ('custom') thanks to our mechanism guess their type just fine. Then only problem is that infact our values which custom columns use are stored in Dictionary<string,object>-like collection and we think that thanks to PropertyDescriptor type for columns is fine, but for some reason FilterItem objects have Value of string. We belive that's because DX filter mechanism can't really guess type of "object" so it uses simple ToString on it so FilterItem.Value is string type instead column's data type.
How to overcome this?
We've found the solution and the mistake was on our side. Column creation etc is fine. Somewhere deep, someone has changed value types.

Apply number format on pivot fields

I am able to apply NumberFormat on pivot fields using Microsoft.Office.Interop.Excel.PivotFields property. But that will not apply same format to summed fields. Is there any separate property for summed fields in pivot table that I am missing ?
Microsoft.Office.Interop.Excel.PivotFields pFields = pivotTable.PivotFields();
foreach (Microsoft.Office.Interop.Excel.PivotField pf in pFields)
if (pf.DataType == XlPivotFieldDataType.xlNumber)
pf.NumberFormat = "#,##0.00";
The documentation on these members isn't exactly crystal clear, but after a bit of digging around and trial and error it looks like the property you want is PivotTable.VisibleFields, which you need to cast to a PivotFields object to iterate through:
Excel.PivotFields vFields = (Excel.PivotFields)pivotTable.VisibleFields;
This will give you all visible fields in the table (as opposed to PivotTable.PivotFields() which only seems to give you the row labels column and the underlying source data). You might have to check the value of each one before you set the number format otherwise I believe it will apply it to everything in the table, which might not be what you want:
foreach (Excel.PivotField pf in vFields)
{
if (pf.DataType == Excel.XlPivotFieldDataType.xlNumber
& pf.Value == "Sum of Price")
{
pf.NumberFormat = "#,##0.00";
}
}
If you wanted to get to one cell in particular, then you can access the PivotField.PivotItems and iterate through those for each PivotField object:
Excel.PivotItems pi = (Excel.PivotItems)pf.PivotItems();
But I think most of the properties of PivotItem are read-only.

Categories

Resources