Can't figure out why this won't work. I get the same "Specified Cast is Invalid" error message. New to C#, be kind. It fails at the if(!((int)WrkRow["ManualWeight"] == 1 | etc. line. I tried a few variations of code, not all pasted here. ManualWeight is a number field in the table.
if (dt.Rows.Count > 0)
{
DataRow WrkRow = dt.Rows[0]; // ds.Tables(0).Rows(0)
if (mod1.IsTareout == true)
trim = (string)WrkRow["Trucker"];
sBarcode = $"{trim.Trim()}{(string)WrkRow["TruckNo"]} ";
XRSwatLaserCert rSwatLaserCert = new XRSwatLaserCert();
rSwatLaserCert.DataSource = dt;
DevExpress.XtraReports.UI.ReportPrintTool rpt = new DevExpress.XtraReports.UI.ReportPrintTool(rSwatLaserCert);
{
XRBarCode XrBCTareOut = new XRBarCode();
rSwatLaserCert.XrBCTareOut = new XRBarCode
{
Text = sBarcode
};
if (!((int)WrkRow["ManualWeight"] == 1 | (int)WrkRow["ManualWeight"] == 3))
{
rSwatLaserCert.XrLabelManualGross1.Visible = false;
rSwatLaserCert.XrLabelManualGross2.Visible = false;
rSwatLaserCert.XrLabelManualGross3.Visible = false;
}
2nd Try:
if (dt.Rows.Count > 0)
{
DataRow WrkRow = dt.Rows[0]; // ds.Tables(0).Rows(0)
if (mod1.IsTareout == true)
trim = (string)WrkRow["Trucker"];
sBarcode = $"{trim.Trim()}{(string)WrkRow["TruckNo"]} ";
XRSwatLaserCert rSwatLaserCert = new XRSwatLaserCert();
rSwatLaserCert.DataSource = dt;
DevExpress.XtraReports.UI.ReportPrintTool rpt = new DevExpress.XtraReports.UI.ReportPrintTool(rSwatLaserCert);
{
XRBarCode XrBCTareOut = new XRBarCode();
rSwatLaserCert.XrBCTareOut = new XRBarCode
{
Text = sBarcode
};
if (WrkRow.Field<int>("ManualWeight") != 1 | (int)WrkRow.Field<int>("ManualWeight") != 3)
{
rSwatLaserCert.XrLabelManualGross1.Visible = false;
rSwatLaserCert.XrLabelManualGross2.Visible = false;
rSwatLaserCert.XrLabelManualGross3.Visible = false;
}
3rd try:
if (dt.Rows.Count > 0)
{
DataRow WrkRow = dt.Rows[0]; // ds.Tables(0).Rows(0)
if (mod1.IsTareout == true)
trim = (string)WrkRow["Trucker"];
sBarcode = $"{trim.Trim()}{(string)WrkRow["TruckNo"]} ";
XRSwatLaserCert rSwatLaserCert = new XRSwatLaserCert();
rSwatLaserCert.DataSource = dt;
ReportPrintTool rpt = new ReportPrintTool(rSwatLaserCert);
{
XRBarCode XrBCTareOut = new XRBarCode();
rSwatLaserCert.XrBCTareOut = new XRBarCode
{
Text = sBarcode
};
var manweight = WrkRow.Field<int>("ManualWeight");
if (manweight != 1 | manweight == 3)
{
rSwatLaserCert.XrLabelManualGross1.Visible = false;
rSwatLaserCert.XrLabelManualGross2.Visible = false;
rSwatLaserCert.XrLabelManualGross3.Visible = false;
}
Take a look at this image, there's a lot going on in it, but hopefully it helps make sense of stuff:
Quick datatable, with a string, int and decimal; John's 20 years old and weighs 10.123 whatevers
When a datatable stores data in its rows, each row is effectively an array of object which means that any time you get a value of a particular column out, it comes out looking like it's an object
See where i've pulled var johnWeight - I used var to let C# decide the type, so I'm not misleading the witness. Take a look in the Locals window (bottom left) - the type is object{decimal} - this means it's a decimal, but boxed inside an object
To get it out as a decimal 10.123 we have to cast using (decimal). This is the only thing you can do at this stage; you can't strip the object wrapping off using any other type, not even one the decimal can be converted to. Check out the decimal johnWeightDecimal = (decimal)johnRow["Weight"] line; that's stripping off the object wrapping and getting the decimal value out. You can see in the locals window that it's a decimal in the type column
If you want it as an int even though it's truly a decimal and wrapped inside object, you first have to pull the object wrapping off using a cast to the true type (decimal) and then cast it again to int using (int). That's the decimal johnWeightInt = (int)(decimal)johnRow["Weight"] line..
So, if you don't know what type your item is, you can either dump it into the Immediate Window (see lower right of screenshot), or you can navigate the Locals window and expand the tree, find the Columns collection, expand it, find the entries, check their DataType..
All this adds up to the reason why plain DataTables are a massive pain in the ass to work with.. There is a solution:
Add a DataSet type of file to your project
Open it, right click, Add a DataTable to it, Add Columns to the datatable, set their datatype
Now you can just use it like any other reasonable class, that has named properties:
.. none of this casting casting casting tedium. dt[0].Weight, not (decimal)dt.Rows[0]["Weight"]
Critically, a PersonDataTable is still a DataTable (it inherits from it) so you can e.g. pass it to some data adapter that is doing SELECT Name, Age, Weight FROM users
var dt = new PersonDataTable();
someDataAdapter.Fill(dt);
There's actually a whole ecosystem within a DataSet to generate type specific DataAdapters too, but that's a bit out of scope for the post
Can you check to see if that column value is null?
Just to be safe, can you convert it to string and use Convert.ToInt32 method?
Can you make a weight variable so you can debug and hover over it to see what it is?
if(!WrkRow.IsNull("ManualWeight"))
{
var weight = Convert.ToInt32(WrkRow["ManualWeight"].ToString());
if(weight == 1 || weight == 3)
{
....
}
}
Related
I am building a BP object that does the following:
Captures text from some rows in an Excel file into a Blueprism collection using the standard Excel VBO.
Puts that collection's contents through a Regex.Match code stage in order to extract a particular value from each row in the collection.
The code stage is then supposed to populate another (output) collection with the extracted values.
Blueprism seems to use c# DataTable objects for collections? I tried using List<> objects, but BP will not implicitly cast those (or anything else for that matter), so guess I have to use DataTable.
The input and output variables in the code below (first and last lines), are the wrappers for the actual BP collections in the BP object.
DataTable localList = input;
DataTable localOutput = new DataTable();
DataRow[] rows = localList.Select();
while (localList != null)
{
for (int i = 0; i < rows.Length; i++)
{
string indexValue = Convert.ToString(localList.Rows[i]);
//example Regex
string sPattern1 = "[0-9]{5,8}[A-Z][A-Z]";
Match match1 = Regex.Match(indexValue, sPattern1);
///same thing with sPattern2, 3, 4, 5 & 6... with corresponding match2, 3, etc...
if (match1.Success || match2.Success || match3.Success || match4.Success ||
match5.Success || match6.Success)
{
localOutput.NewRow();
localOutput.Rows.Add(indexValue);
}
else
{
localOutput.NewRow();
localOutput.Rows.Add("Not Found");
}
}
}
output = localOutput.Select();
So, this code compiles OK, i.e. doesn't throw any type-cast errors or anything like when I tried to use List<>, but then when I run the object BP throws the following runtime exception:
Internal : Could not execute code stage because exception thrown by code stage: Input array is longer than the number of columns in this table.
What am I doing wrong? I'd appreciate any help with this, thank you.
First, you have to define the columns:
DataTable localOutput = new DataTable();
localOutput.Columns.Add("Column1", typeof (string));
Then you can add new rows.
Does anyone have an idea why the source code has the following behavior (there are only two data sets after an inner join): The two data sets are read correctly. But now I have the second dataset as result in both datasets. I just can't figure it out.
for (int x = 0; x < ds.Tables[0].Rows.Count; x++)
{
tasks.ID = (int)ds.Tables[0].Rows[x]["ID"];
tasks.UserID = (int)ds.Tables[0].Rows[x]["UserID"];
tasks.Aufgabe = (int)ds.Tables[0].Rows[x]["Aufgabe"];
tasks.Start = (DateTime)ds.Tables[0].Rows[x]["Start"];
tasks.Ende = (DateTime)ds.Tables[0].Rows[x]["Ende"];
tasks.Erledigt = (ds.Tables[0].Rows[x]["Erledigt"] == DBNull.Value) ? DateTime.MinValue : (DateTime)ds.Tables[0].Rows[x]["Erledigt"];
tasks.AufgabenID = (int)ds.Tables[0].Rows[x][6];
tasks.Bezeichnung = ds.Tables[0].Rows[x]["Bezeichnung"].ToString();
tasks.Beschreibung = ds.Tables[0].Rows[x]["Beschreibung"].ToString();
tasks.Zeitrahmen = (int)ds.Tables[0].Rows[x]["Zeitrahmen"];
tasks.Wert = (decimal)ds.Tables[0].Rows[x]["Wert"];
tasks.WertGutschrift = (int)ds.Tables[0].Rows[x]["WertGutschrift"];
tasks.Folgeaufgabe = (int)ds.Tables[0].Rows[x]["Folgeaufgabe"];
taskList.Add(tasks);
}
You are adding the same object several times. Create a new one in every iteration (as first line in the for loop):
Task tasks = new Task();
You set all properties of one object and add it to the list. In the next iteration of the loop, you update the existing object with new values and add it again to the list.
P.S: Don't forget to remove the line, where you are creating the object right now, or you will get a compiler error.
I have a datagridview with a combobox column that is bound to an enum as follows:
var D = (DataGridViewComboBoxColumn)dgvInputs.Columns[2];
D.ValueType = typeof(MyType);
D.ValueMember = "Value";
D.DisplayMember = "Display";
D.DataSource = new MyType[] {
MyType.Rev,
MyType.Model,
MyType.User,
MyType.Status
}.Select(x => new { Display = x.ToString(), Value = (int)x }).ToList();
The datagridview is then bound to a DataTable named ParameterTable:
BindingSource ParamSource = new BindingSource();
ParamSource.DataSource = DataEntry.ParameterTable;
dgvInputs.AutoGenerateColumns = false;
dgvInputs.DataSource = ParamSource;
dgvInputs.Columns[0].DataPropertyName = "Name";
dgvInputs.Columns[1].DataPropertyName = "Prompt";
dgvInputs.Columns[2].DataPropertyName = "Type";
dgvInputs.Columns[3].DataPropertyName = "Width";
dgvInputs.Columns[4].DataPropertyName = "Default Value";
When the user finishes editing the table, I need to validate it. In particular, I need to test that the Type has been defined in each row, and that the Default Value is compatible with the Type.
The problem is, every test I've found for checking if the Type has been set has failed. When I later try to cast the value as MyType as part of testing the default value, I get an error. When I check the .Value property on the empty Type cell in the debugger, it shows a value of "{}".
Currently, I have this code for the test, in the Validating event for the datagridview itself. I have tried various other versions and they have also failed:
foreach (DataGridViewRow Row in dgvInputs.Rows) {
if (!Row.IsNewRow) {
// test other columns ...
DataGridViewComboBoxCell Cell = (DataGridViewComboBoxCell)(Row.Cells[2]);
if (Cell == null || Cell.Value as string == string.Empty) {
// Error ...
}
MyType PType = (MyType)(Cell.Value);
How can I test if a DataGridViewComboBox cell has not been set, and what is this value "{}"?
FYI - I am using VS 2008, and .Net 3.5 SP1. Not my choice. Just what is available to me.
There are a couple problems with this code.
First, D.ValueType = typeof(MyType); is incorrect because from what I see, you are binding to int field. Just remove that line, ValueType will be inferred from the data source.
Now, the main issue. When binding to a data table, the non entered value is represented by DBNull.Value. I would suggest you checking for both null and DBNull.Value. When entered, the value type in your case will be int, but you can safely unbox it to MyType.
The code should be something like this
//...
var value = Row.Cells[2].Value;
if (value == null || value == DBNull.Value)
{
// Error ...
}
else
{
var type = (MyType)value;
// Check if type contains a valid value ...
}
This question already has answers here:
Is it possible to dynamically compile and execute C# code fragments?
(7 answers)
Closed 9 years ago.
I want to execute a C# statement which is stored in a string variable. For instance:
string statement1 = "button1.Visible = true";
string statement2 = "button1.Text = \"Number\"";
Looking at your comments and that you have 80 controls requiring very similar action, dynamic compilation may be an overkill for this purpose. You can use use Controls collection of the parent container along with the Tag property of your buttons to achieve it. A single event handler would suffice.
You can use LINQ members like OfType and Cast to make your code even smaller.
Edit
After looking at your latest comment, what you should do is to programmatically create your buttons and add them to your Form or whatever container you have. You can then keep a Dictionary<string, Button> that will let you either iterate over the collection, or access an individual button through its name. Something like:
//Declare this globally
Dictionary<string, Button> Dic = new Dictionary<string, Button>(81);
//put this in the constructor
for(int i=0; i<81; i++)
{
Button b = new Button();
b.Text = i; //or Random or whatever
b.Name = "btn" + i.ToString();
this.Controls.Add(b);
Dic.Add(b.Name, b);
}
Later you can do both iteration like this:
foreach(var item in Dic)
{
item.Value.Visible = true; //or false
}
and key-based access like this:
Dic["btn45"].Visible = true; //or false
Since you're creating Sudoku, i probably think you should use TableLayoutPanel with appropriate number of rows and columns at design-time and then add your buttons to the panel and set their Row/Column property in the loop. This will help better respond to resizing events etc.
From your comments it appears you simply want to iterate over 80 similar buttons and configure them the same way. This can be accomplished by far simpler means than executing dynamic code.
Here's a couple of ways.
Use the tag property
First set the Tag property to a specific number in the designer for each of the 80 buttons.
Then execute this code:
foreach (var button in Controls.OfType<Button>().Where(button => button.Tag == "TAG"))
{
button.Visible = true;
button.Text = "Number";
}
Use the name to identify them:
Ensure all the 80 buttons have a name of "buttonX" where X is a number.
Then execute this code:
foreach (var button in Controls.OfType<Button>().Where(button => button.Name.StartsWith("button"))
{
button.Visible = true;
button.Text = "Number";
}
Actually "execute code"
If, as you say in your comments, you only need to solve this problem: "Execute" this type of code:
object.member=value
Where object refers to something stored in a field or property. member refers to a public member (field or property) of that object, and value is something that will always be easily convertible to the member type, then the following code will work.
Note that it is short on error checking, so please make sure you vet the code before using it.
ALSO I am not convinced in the very slightest that this is the appropriate solution, but since you've decided to ask about the solution you had in mind instead of the actual problem, it's hard to come up with a better solution.
You can run this code in LINQPad:
void Main()
{
button1.Dump();
SetProperties(this,
"button1.Visible=true",
"button1.Text=\"Number\""
);
button1.Dump();
}
public static void SetProperties(object instance, params string[] propertySpecifications)
{
SetProperties(instance, (IEnumerable<string>)propertySpecifications);
}
public static void SetProperties(object instance, IEnumerable<string> propertySpecifications)
{
var re = new Regex(#"^(?<object>[a-z_][a-z0-9_]*)\.(?<member>[a-z_][a-z0-9_]*)=(?<value>.*)$", RegexOptions.IgnoreCase);
foreach (var propertySpecification in propertySpecifications)
{
var ma = re.Match(propertySpecification);
if (!ma.Success)
throw new InvalidOperationException("Invalid property specification: " + propertySpecification);
string objectName = ma.Groups["object"].Value;
string memberName = ma.Groups["member"].Value;
string valueString = ma.Groups["value"].Value;
object value;
if (valueString.StartsWith("\"") && valueString.EndsWith("\""))
value = valueString.Substring(1, valueString.Length - 2);
else
value = valueString;
var obj = GetObject(instance, objectName);
if (obj == null)
throw new InvalidOperationException("No object with the name " + objectName);
var fi = obj.GetType().GetField(memberName);
if (fi != null)
fi.SetValue(obj, Convert.ChangeType(value, fi.FieldType));
else
{
var pi = obj.GetType().GetProperty(memberName);
if (pi != null && pi.GetIndexParameters().Length == 0)
pi.SetValue(obj, Convert.ChangeType(value, pi.PropertyType));
else
throw new InvalidOperationException("No member with the name " + memberName + " on the " + objectName + " object");
}
}
}
private static object GetObject(object instance, string memberName)
{
var type = instance.GetType();
var fi = type.GetField(memberName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default);
if (fi != null)
return fi.GetValue(instance);
var pi = type.GetProperty(memberName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default);
if (pi != null && pi.GetIndexParameters().Length == 0)
return pi.GetValue(instance, null);
return null;
}
private Button button1 = new Button();
public class Button
{
public bool Visible;
public string Text { get; set; }
}
This will output (the two button1.Dump(); statements) the button configuration before and after, and you'll notice that the property and the field have been set.
You can execute this as follows:
`SetProperties(this, "...", "...");
where this must refer to your form object (the one that owns the buttons).
What about using delegates?
Action statement1 = () => button1.Visible = true;
Action statement2 = () => button1.Text = "Number";
I am currently using one button for inserting/updating content within a table. It then takes the uploaded CSV and inserts or updates it into a data table depending on whether the row exists or not.
Here is the code fired after the button's OnClick:
if (ExcelDDL.SelectedValue == "Time Points" && fileName == "TimePoints.csv")
{
var GetTPoints = (SEPTA_DS.TimePointsTBLDataTable)tpta.GetDataByCategory(CategoryDDL.SelectedItem.ToString());
//Loop through each row and insert into database
int i = 0;
foreach (DataRow row in TempRouteDataTable.Rows)
{
//Gather column headers
var category = Convert.ToString(CategoryDDL.SelectedItem);
var agency = Convert.ToString(row["Agency"]);
if (agency == null || agency == "")
{
//If row is empty skip it entirely
goto skipped;
}
var route = Convert.ToString(row["Route"]);
var GetRShortName = (SEPTA_DS.RoutesTBLDataTable)rta.GetDataByRouteID(route);
var newRoute = "";
if (GetRShortName.Rows.Count > 0)
{
newRoute = Convert.ToString(GetRShortName.Rows[0]["route_short_name"]);
}
var direction = Convert.ToString(row["Direction"]);
var serviceKey = Convert.ToString(row["Service Key"]);
var language = Convert.ToString(row["Language"]);
var stopID = Convert.ToString(row["Stop ID"]);
var stopName = Convert.ToString(row["Stop Name"]);
if (stopName.Contains("accessible"))
{
string[] noHTML = stopName.Split('>');
int insertH = Convert.ToInt32(hta.InsertHandicapRow(newRoute,noHTML[2]));
}
var sequence = Convert.ToString(row["Sequence"]);
var origID = -1;
if (GetTPoints.Rows.Count > 0)
{
origID = Convert.ToInt32(GetTPoints.Rows[i]["TPointsID"]);
var GetID = (SEPTA_DS.TimePointsTBLDataTable)tpta.GetDataByID(origID);
if (GetID.Rows.Count < 1)
{
origID = -1;
}
}
if (origID == -1)
{
int insertData = Convert.ToInt32(tpta.InsertTimePoints(category, agency, newRoute, direction, serviceKey, language, stopID, stopName, sequence));
}
else
{
int updateData = Convert.ToInt32(tpta.UpdateTimePoints(category, agency, newRoute, direction, serviceKey, language, stopID, stopName, sequence, origID));
}
skipped:
i++;
}
}
You can see how I check whether to insert or update around the bottom. I am using this method across other sections of this program and it works just fine. But in this case it is distorting my datatable immensely and I can't figure out why.
This is the bottom part of my table after inserting [no items currently within the database]:
This is the table after reuploading the CSV with data already existing within the table:
I am also getting this error when updating There is no row at position 2230.
What is going wrong in the code to cause this huge shift? I am just checking to see if the ID exists and if it does update rather than insert.
Also the reason i am using goto is because there are blank rows in the document that need to be skipped.
Is your TPointsID column, a auto-generated number? If so, since you are skipping the empty row, some referential integrity problem might be occuring,because of empty data in the skipped rows in the database.
From the error : There is no row at position 2230 , it is also understood that, because of the skipping you might be trying to access some non existent row in the datatable.
Also, if possible consider using the ADO.NET DataAdapter which has got the CRUD operation capability. You can find more about it at : http://support.microsoft.com/kb/308507