create instance of DataColumnChangeEventArgs in C# - c#

I am trying to write a function as follows:
private void Func1(DataColumnChangeEventArgs e)
{
ds.TableName.AddRow(
e.Row[e.Column.ColumnName, DataRowVersion.Original].ToString(),
e.Row[e.Column.ColumnName, DataRowVersion.Proposed].ToString());
}
and I am calling it as:
private void Func2()
{
DataColumnChangeEventArgs e = new DataColumnChangeEventArgs(
dataTable.Rows[index],
dataTable.Columns["ColName"],
newValue);
e.ProposedValue = newValue;
Func1(e);
}
However, e.Row[e.Column.ColumnName, DataRowVersion.Proposed].ToString() is throwing a VersionNotFoundException. Is there any way to achieve this?

I would say, your method should look like this:
ds.TableName.AddRow(e.Row[e.Column.ColumnName].ToString(), e.ProposedValue.ToString());
As there is no versions in that row in your row in args, but there is new and old values...

DataColumnChangedEventArgs is designed to be used with a class like DataTable. The DataTable creates an instance when calling the associated events (like ColumnChanged). Creating an instance of one will not actually create the change to the data row.

The e.ProposedValue = newValue is redundant, you already gave the newValue in the constructor.
You can access the value through e.ProposedValue. So through this system you can only make one change to the row at a time and you must remember which column it was.

Related

Read assembly MethodBody, parse its Name in a ListBox and its IL in a TextBox?

I have a WPF MainWindow Form with a ListBox and a TextBox that looks like this:
Figure A. WPF MainWindow with Sample Text.
Now, the Load Assembly... OnClick button event allows me to select a .NET Assembly and load it up using DnLib
Then, if I want to display the Methods bodies I would do it like so:
Assembly asm = Assembly.LoadFile(filename);
foreach (Module mod in asm.GetModules())
{
foreach (Type types in mod.GetTypes())
{
foreach (MethodInfo mdInfo in types.GetMethods())
{
listBox.Items.Add(mdInfo.Name);
}
}
}
This adds each found Method name to the ListBox on the left, resulting like so:
Figure B. Showing the ListBox Filled with Methods Names
Now the trick part, I would like to for whichever method I select from the ListBox to display its respective MethodBody IL on the TextBox
How can I achieve such thing?
«Phew!» Finally Solved it!
Here's the solution for whoever tries to do the same thing in the future.
Make an instance of 'List' and then iterate through the methods and assign the names to such list, then whenever your SelectedItem index value changes, I can simply call GetMethodBodyByName and then I can surely solve this issue
Here's how to implement the function GetMethodBodyByName:
public string GetMethodBodyByName(string methodName)
{
ModuleDefMD md = ModuleDefMD.Load(filename);
foreach (TypeDef type in md.Types)
{
foreach (MethodDef method in type.Methods)
{
for (int i = 0; i < type.Methods.Count; i++)
{
if (method.HasBody)
{
if (method.Name == methodName)
{
var instr = method.Body.Instructions;
return String.Join("\r\n", instr);
}
}
}
}
}
return "";
}
The idea is that 'GetMethodBodyByName' will receive the method name as a parameter, then it will iterate through methods and see if a method matches the given name, then if found, the function will just simply iterate through that method and output the method's body.
Here's how my ListBox_SelectedItemChanged event looks like:
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
textBox.Text = "";
textBox.Text = GetMethodBodyByName(method[listBox.SelectedIndex].Name);
}
That's All Folks!
Note: Be careful when doing this approach as if when you request names, different methods can have the same names. But that's a cake for another day, I'm done for now! take care bye-bye!
Working our way up for the Ultimate Solution!
The WPF MainWindow Forms carry with themselves two little useful properties, they are: Tag and Content, the idea is the following one:
With the Tag and Content Property we can assign any values to it that later it can be retrieved On-The-Fly without having to depend on Methods names specifically for this task.
So you would instead of looping each method and get its name respectively you can just do the way I did:
Iterate through the Method, and assign its body to the Tag property, and its name to the Content property, as this last property is the one that handles the actual Title property, so disregarding anything you do with the method in the future and even if it had the same name of another one, it will work no matter what.
How Can We Implement It?
Simply:
<...>
// Inside Method Body iteration routine...
<...>
var instr = mdInfo.Body.Instructions;
// Allocate in a new `ListBoxItem` each method and add it to the current listbox with their
// ... respective Tag and Content information... // Many Thanks Kao :D
newItem = new ListBoxItem();
newItem.Content = mdInfo.Name;
newItem.Tag = string.Join("\r\n", instr);
method.Add(mdInfo);
listBox.Items.Add(newItem);
Then on your SelectedItem Index-Value-Changed Event put this:
MSILTextBox.Clear();
// Retrieve them given the selected index...
// ... the returned value will be the Tag content of the ...
// ... previously saved item.
string getTag= ((ListBoxItem)listBox.SelectedItem).Tag.ToString();
MSILTextBox.Text = getTag;

Globally Declared DataTable Already Belongs to Another DataSet

I am having problems understanding some logic but I am excited to learn what is goin on. I have an application that uses a third party web service to execute xml and receive response back, no problems here. I have some DataSets and DataTables that are declared globally. The reason I have done this is because these DataSets and DataTables will not change but need to be accessed from other methods. What happens is the form loads and my DataGridView populates just fine but when I select a different date from myComboBox the code throws an exception stating that the DataTable already belongs to another DataSet. Here is a simplified sample of what I am working with:
public class Test
{
private BusinessLayer businessLayer;
private int id;
private List<int> employees;
private DataSet employeeInfoDataSet;
private DataSet employeesTimeDataSet;
private DataTable employeeInfoDataTable;
private DataTable employeesTimeDataTable;
public Test()
{
businessLayer = new BusinessLayer();
id = 3;
// these should never change
// I almost thought about making them static
employees = businessLayer.getEmployees(id);
employeeInfoDataSet = businessLayer.getEmployeeInfoDataSet(employees);
employeeInfoDataTable = businessLayer.getEmployeeInfoDataTable(employeeInfoDataSet);
employeeInfoDataTable.TableName = "EmployeeInfo";
string date = myComboBox.SelectedValue.ToString();
initDataTable(date);
bindDataGridView();
}
private void initDataTable(string date)
{
employeesTimeDataSet = businessLayer.getEmployeesTime(employees, date);
employeesTimeDataSet.Tables.Add(employeeInfoDataTable); // <-- errors here
employeesTimeDataTable = businessLayer.buildEmployeesTimeDataTable(employeesTimeDataSet);
}
private void bindDataGridView()
{
dgv.DataSource = timesheetsDataTable;
}
private void myComboBox_SelectionChangeCommitted(object sender, EventArgs e)
{
string date = myComboBox.SelectedValue.ToString();
initDataTable(date);
bindDataGridView();
}
}
I am struggling to understand why it runs fine when the form loads but throws this exception when I change the date. Can someone help me understand what is causing this? Many, many thanks!
PS: The businessLayer.getEmployeeInfoDataTable and businessLayer.buildEmployeesTimeDataTable methods build the DataTables programmatically so there shouldn't be the "return ds.Tables[0]" issue where you need to use DataTable.Copy()...thanks :)
The error-message is self-explanatory, isn't it? You are trying to add the same table to another DataSet which you've already added to one in the constructor. You cannot add the same table(same reference) to two different DataSets. So what are you trying to do?
Maybe you want to check if it's already in a DataSet:
if( employeeInfoDataTable.DataSet == null)
employeesTimeDataSet.Tables.Add(employeeInfoDataTable);
Maybe you want to include this table into the DataSet in the first place which seems to be the best option if possible.
Or maybe you want to remove the table from the DataSet before you create it again. On this way the DataSet property of the table will be "cleared"(you cannot assign null) and you can add the table later to the newly created DataSet.
So like this:
private void initDataTable(string date)
{
employeesTimeDataSet.Tables.Remove(employeeInfoDataTable);
employeesTimeDataSet = businessLayer.getEmployeesTime(employees, date);
employeesTimeDataSet.Tables.Add(employeeInfoDataTable); // <-- now it works
employeesTimeDataTable = businessLayer.buildEmployeesTimeDataTable(employeesTimeDataSet);
}
Note that this exception is similar to the one that is raised if you're trying to add the same table twice to the same DataSet. Both is checked and not allowed.

Passing class property as a parameter of a method

I want to refactor some of my code, so I would like to create method for few repeating tasks. One of methods, depending on context, include working something with properties. Most of properties are string, but there are also enum and int types.
For example, method should look like this:
private void someMethod (int i, 'here should be property') {
//enter code here
}
So, does anybody know how to pass this properties?
Thanks in advance!
Another explanation. This code should change label properties: text, font... But, label.Text should be changed depending on entry parameter.
it should look like this
private void setLabel (Label label, 'I dont know what goes here to pass a property') {
label.Text = user.'property'.toString();
//some more code
}
If you don't have any particular reason why you want to actually pass the property, you can just pass the value of that property:
private void setLabel (Label label, object propertyValue)
{
label.Text = propertyValue.ToString();
}
And then call it like:
setLabel(myLabel, user.ThePropertyIWant);
You should be able to do such thing with reflection:
http://www.dotnetperls.com/reflection-field

Why doesn't my combobox display text?

I'am working in a GTK-sharp application. I have this code but combobox1 doesn't display any item. Why not?
ListStore store = new ListStore(typeof(myclass));
store.AppendValue(new myclass("hola",7));
store.AppendValue(new myclass("hola2",8));
store.AppendValue(new myclass("hola3",2));
combobox1.Model = store;
The class myclass overrides ToString()
What you are looking for is custom Gtk.CellRenderer:
private void MyClassRenderer(CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter)
{
MyClass myclass = model.GetValue(iter, 0) as MyClass;
(cell as CellRendererText).Text = myclass.ToString();
}
With some additional code in the setup method like this:
CellRendererText myClassCell = new CellRendererText();
combobox1.PackStart(myClassCell, true);
combobox1.SetCellDataFunc(myClassCell, MyClassRenderer);
ListStore store = new ListStore(typeof(MyClass));
store.AppendValues(new MyClass("hola",7));
store.AppendValues(new MyClass("hola2",8));
store.AppendValues(new MyClass("hola3",2));
combobox1.Model = store;
Make sure the SetCellDataFunc method is called after PackStart method.
Job done! :)
I'm not really sure, but make sure the listbox key and values are mapped to the fields in the class. I think it needs to be specific. After setting the value, make sure to do the final databind like: control.DataBind();
In general, C# binding goes like: 1) automatic colum generation/manually map all fields to keys and values 2). set the field 3. and call the bind() function.

how to avoid rebinding grid each time the control is initialized?

I've created a custom user control with a grid. I'd like to bind this grid once, and use it over and over again in my app. If I put the binding within the control, the data is retrieved as many times as I use the control. How do I bind it only once??
public ClientLookUp()
{
InitializeComponent();
vw_clientsTableAdapter.Fill(dsclientlkup.vw_clients); //This occurs as many times as I have the user control, instead of just once.
}
Well anything you put in the constructor will be executed every time you construct the object!
What about providing an Initialize method that you can call whenever you need to reload the data??
If you want to load the data only once, then load it either into a static variable or a separate class that is referenced by the control.
If you really want to use the same single grid in your control over and over, you could create a single, static grid, and have your ClientLookUp constructor add it to the right place—Panel, or whatever—whenever a new one is created.
Before you go do this road however, ask yourself if this is really what you want to do. Having the same identical grid existing in many places may cause you problems down the road. If you want to support in-grid editing, you'll find that changing one value changes the identical value in all your other grids..
EDIT
I tried getting the below code to work, but I'm not sure this approach will be possible. It seems as though the minute you try to attach the same UI element into more than one place, it gets moved out of the last place you put it; it doesn't look like you can have the same grid being in more than one place at once. This makes sense when you think about it.
Here's the code I tried. Maybe it will be of some use to you.
public UserControl1()
{
InitializeComponent();
this.Controls.Add(myStaticGridView);
myStaticGridView.Dock = DockStyle.Fill;
}
static DataGridView _staticGrid;
public DataGridView myStaticGridView
{
get
{
if (_staticGrid != null)
return _staticGrid;
_staticGrid = new DataGridView();
_staticGrid.Columns.Add("A", "A");
_staticGrid.Columns.Add("B", "B");
_staticGrid.Columns.Add("C", "C");
_staticGrid.Columns[0].DataPropertyName = "A";
_staticGrid.Columns[1].DataPropertyName = "B";
_staticGrid.Columns[2].DataPropertyName = "C";
_staticGrid.DataSource = new[] {
new { A = "someA", B = "someB", C = "someC"},
new { A = "someA", B = "someB", C = "someC"},
new { A = "someA", B = "someB", C = "someC"},
new { A = "someA", B = "someB", C = "someC"},
};
return _staticGrid;
}
}
And then loading the control like this:
private void button1_Click(object sender, EventArgs e)
{
flowLayoutPanel1.Controls.Add(new UserControl1());
}

Categories