Communicating between forms - winforms

I have a main form from which I generate multiple subforms. I store these forms in a List<Subform^> ^ variable so that I can send messages to them from the main form. I load new forms like this (from memory, might not compile):
Subform ^sf = gcnew Subform(some, variables, here);
subforms->Add(sf);
subforms[subforms.Count-1]->Show();
My goal is to remove the subform from the list once it's closed. I've been considering moving to a dictionary for simpler form identification, like this:
++i; // Some sort of a form counter. to access them when closing.
Subform ^sf = gcnew Subform(some, variables, here);
subforms->Add(i, sf);
subforms[i]->Show();
How would I remove the ith form when closing? Perhaps something like this (in pseudocode)
sf->FormClosed = subforms->RemoveAt[i]; // Before I add it to the dictionary.
?

Try something like:
sf->FormClosed += gcnew FormClosedEventHandler(this, &RemoveSubform);
void RemoveSubform(System::Object^ sender, FormClosedEventArgs^ e)
{
subforms->Remove(sender);
}

Related

How a customized DB field is saved to DB with in the graph-Acumatica

Suppose POLine has a UsrCustomfield and also have some calculation to populate the value for the UsrCustomfield based on other fields in the same POLine.
Which event handlers are used to populate and save the value to the DB? Saving has to be done from Graph itself... But from where?
You have multiple ways to calculate the value of a custom field.
The first method is using a PXFormula attribute. This can work well if you have an easy query to calculate the value of the field. This would be set on the DAC extension of POLine that you declared the UsrCustomfield as an attribute. The help article has many different ways to accomplish this.
https://help-2018r1.acumatica.com/Wiki/(W(59936))/ShowWiki.aspx?pageid=1d25dc74-747c-4d44-8ad9-033e5a476b6f
Another way to accomplish this is trigger events based on the fields, or row, updating to re-calculate. For example, if you want to re-calculate every time the row is updated with ANY field, you can use the function:
public virtual void POLine_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e, PXRowUpdated del)
{
//first, invoke the base method. can be placed anywhere in your code.
del?.Invoke(sender, e);
//get the row
POLine line = (POLine)e.Row;
if (line == null) return; //always good to check if the object is not null
//get the DAC extension from the object
POLineExt lineExt = line.GetExtension<POLineExt>();
decimal? CalculatedValue = 0;
//do some calculation to figure out what the value should be
//compare the new value against the existing value
if(lineExt.UsrCustomField != CalculatedValue)
{
//set the new value
sender.SetValueExt<POLineExt.usrCustomField>(line, CalculatedValue);
}
}
The issue with this is that it will call the code every time a POLine is updated. This may be too many calls. You can use functions to update each individual line using a FieldUpdated event on each field to recalculate as well. For example, you may want to check ExtCost:
public virtual void POLine_ExtCost_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e, PXFieldUpdated del)
{
//similar code as above
}
This method is more code, and more complex, but may be needed if your logic cannot fit into a PXFormula attribute.

Graph keeps updating based on the new values in the database in visual c++ via thread programming

I have a database and a winform application in visaul c++. The db has two columns Date and Temp. These values are automatically inserted in the db by a different c++ program which is scheduled to run every 2-3 second. In the form there is a button "Show Plot" which on clicking would show the Date vs Temp graph. I am able to do this. However what i want is that this graph keeps updating based on the new values in the database...something like a heart beat monitor or to that effect. How can i achieve this. Please advice how can i do this using winform project in visual c++ and THREAD programming
Reards
PS: As i am using Winform application in visual c++ a lot of code is generated for th picture elemnets. Some part which may help are:
private: System::Void temperature_btn_Click(System::Object^ sender, System::EventArgs^ e) {
String^ constring = L"datasource=localhost;port=3306;username=root;password=root;";
MySqlConnection^ conDataBase = gcnew MySqlConnection(constring);
MySqlCommand^ cmdDataBase = gcnew MySqlCommand("select * from `data`.`test`;",conDataBase);
MySqlDataReader^ myReader;
try{
conDataBase->Open();
myReader = cmdDataBase->ExecuteReader();
// MessageBox::Show("Data Inserted");
while(myReader->Read()){
String^ v_datetime;
String^ v_pressure;
v_datetime = myReader->GetString("datetime");
v_pressure = myReader->GetInt32("temp").ToString();
String^ status;
if (myReader->GetInt32("temp") > 1000 && myReader->GetInt32("temp") < 50 )
{
status = " Abnormal ";
this->chart2->Series["Temperature"]->Color = System::Drawing::Color::Red;
}
else{
status = " Normal";
}
this->label3->Text = status;
this->chart2->Series["Temperature"]->Points->AddXY(v_datetime,myReader->GetInt32("temp"));
// comboBox1->Items->Add(vName);
}
}catch(Exception^ex){
MessageBox::Show(ex->Message);
}
}
What needs to be done to make this dynamic...i.e, the chart keeps picking values from the database at regural intervals...say every 3-5 seconds (the db is being updated by another completely unrelated process every 2-3 second)
PS EDIT 2: sorry...i should have made it clear... how to do this by threads...i apologise once again for not being clear
Add a Timer to your Windows Form (from the Toolbox) and register an EventHandler for the Tick event. Also set the "Intervall" to a value you want. Then either set the property "Enabled" to "true" or start it manually in the "Form_Loaded" EventHandler.

Hierarchy data binding to grid in a windows application

I have data setup like this..
Transaction
-Name
-ID
-Amount
-Tags <-- Array of Tags
- Name
In a website, I could just loop through the tags and create spans of each tag with a link, I'm trying to figure out how to add this structure to a windows form application inside a XtraGrid (DevExpress)
Wanting it be like this..
ID
Name
Amount
Tags
1
MyTran
13.02
tag1 tag2 <-- each tag would be a linklabel with seperate event calls
I'm not sure how to accomplish this.
You can use the GridView.CustomDrawCell Event to format you display text, but you should not edit these tags using the gridview editors. you can change the display text of the cell (the RowCellCustomDrawEventArgs.DisplayText parameter.
private void advBandedGridView1_CustomDrawCell(object sender, RowCellCustomDrawEventArgs e)
{
//GridView currentView = sender as GridView;
//if(e.RowHandle == currentView.FocusedRowHandle) return;
if (e.Column.FieldName != "Tags") return;
string[] arr = (string[])e.CellValue;
string csv = String.join(',', arr);
e.DisplayText = csv;
// set e.Handles to true if you want custom drawing..
e.Handled = true;
}
Check the custom drawing section links specified on GridView.CustomDrawCell Event documentation page.
Another approach is that you store comma separated tags in you table
which you are binding with grid control. then you can edit those
without any problem. you have handle update, insert event with some
customizations and this will be much better than displaying custom
text.
Choose which approach suite you better. Hope this help.

Is there a simple way to set a large number of WPF Form objects?

This is just basic example:
if (a == b)
{
textBox1.Text = "a == b";
textBox1.IsEnabled = true;
textBox2.Text = "EXAMPLE2";
textBox2.IsEnabled = false;
textBox3.Text = "EXAMPLE3";
textBox3.IsEnabled = false;
textBox4.Text = "EXAMPLE4";
textBox4.IsEnabled = false;
textBox5.Text = "EXAMPLE5";
textBox5.IsEnabled = false;
}
else
{
textBox1.Text = "a != b";
textBox1.IsEnabled = false;
textBox2.Text = "EXAMPLE2";
textBox2.IsEnabled = true;
textBox3.Text = "EXAMPLE3";
textBox3.IsEnabled = true;
textBox4.Text = "EXAMPLE4";
textBox4.IsEnabled = true;
textBox5.Text = "EXAMPLE5";
textBox5.IsEnabled = true;
}
This seems pretty tedious. I was wondering if there's a better way to handle situations like that? I just have this feeling that all the Pros out there don't do things using this 1x1x1x.... method.
There are lots of ways to refactor this sort of code to give something more succinct. It really does become a matter of experience and preference to choose the method that is appropriate for each case.
To that end, I've listed below several options that I find serve me well:
Binding
Binding particularly within WPF gives a very powerful way of easily updating form objects. In your Xaml you apply a datacontext (generally for the form/window) and bound property for each object which then allows you to then update properties in your c# code and have them automatically reflected in the form objects.
One big advantage of this is getting all that tedious object.Text = "xyz" code out of the way.
In your given example, you could introduce a boolean property TextBoxesEnabled which is bound to the enabled property of your text boxes, and reflects whether a == b.
Domain objects
This follows on from the binding approach—in your example above it looks like all your properties are dependant of only two external pieces of data ("a" and "b"). In this situation your are often limited to doing something just like you have, where you need to set your UI properties within lots of procedural code.
However, more often you can introduce expressive domain objects that have properties that map directly to UI properties. For example, perhaps you have a User object, with a Name property, and then a user window, with a name textbox.
Now your domain object will be provided by calls into your service layer and data access infrastructure, and due to binding automatically displayed in your UI.
Code refactoring
Even where you are needing to set lots of properties directly without the ability to use more expressive objects and binding, there will often be some simple refactorings that can help.
In the code example you gave, two easy refactorings leap out to me:
a == b is a Boolean evaluation so you could have something like:
bool enableTextBoxes = a == b;
TextBox1.Enabled = enableTextBoxes;
TextBox2.Enabled = enableTextBoxes;
// etc...
You are setting the text to the same thing in the branches of the if, so take that out of the if and just have it once (though I'm guessing this is just because you were giving trivial example code).
You can loop over collections of controls, so if you need to set large numbers of control values to the same thing you can just do iterate over them and assign to each.
Assuming:
1. your controls have a pattern myControl1, myControl2, etc. (e.g textBox1, textBox2, etc)
2. your controls are all children of same FrameworkElement parent (e.g. all of them are within a Grid)
you can try something like:
bool shouldEnable = (a == b); //or whatever logic you want to have to decide the enable/disable value
for(i=0; i<10; i++)
{
string myControlName = "textBox" + i.ToString(); //assuming your pattern to be textBox1, textBox2, etc.
object myControl = myGrid.FindName(myControlName); //assuming your textboxes present in a Grid named "myGrid"
if(myControl is TextBox) //assuming your controls to be TextBoxes
{
TextBox myTextBox = (TextBox)myControl;
myTextBox.IsEnabled = shouldEnable;
myTextBox.Text = "whatever you want based on your logic";
}
}
Do remember:
1. to refer to FindName on MSDN: FrameworkElement.FindName
2. in case all of your controls are not under one parent, you might want to have a look at: How can I find WPF controls by name or type?
3. to be aware of performance implications (you may want to check it).
Also see: Find Controls by Name in WPF

TDD with DataGridView

I'm relatively new to TDD, and still trying to learn to apply some of the concepts. Here's my situation.
I've got a WinForm with a DataGridView. I'm trying to write a test for the routine to be called by a button click that will perform some operations on the selected rows of the grid.
So I will be passing in the DataGridViewSelectedRowCollection object (i.e, the dgv.SelectedRows property at the time the button is clicked).
The DataGridViewSelectedRowCollection object has no constructor, so the only way I can figure to create it is to put together a DataGridView in my test project, then select some rows and pass in the SelectedRows property. But clearly, I don't want to re-create the whole form there.
So I do a DataGridView dgv = new DataGridView(), and gin up a BindingList (actually a SortableBindingList) just like the grid is bound to in the real application. The test list has 3 rows in it. And I do a dgv.DataSource = myList.
Now, at that point in the real application, the grid view is bound. If I look at dgv.Rows.Count, it's equal to the number of rows in the list. However, in my test, setting the DataSource property to the list still results in zero rows in the grid.
I'm thinking there's something missing in the creation of the gridview that normally gets done when it's added to the control list of the form. It probably initializes the handler for the OnDataSourceChanged event or something, and that isn't being done in my test code, but I'm really at a loss as to how to fix it, again, without re-creating a whole form object in my test fixture.
Here's the relavant code form my test method:
DataGridView residueGrid = new DataGridView();
List<Employee> baseListToGrid = new List<Employee>();
SortableBindingList<Employee> listToGrid = new SortableBindingList<Employee>(baseListToGrid);
residueGrid.DataSource = listToGrid;
for (int ix = 1; ix < 4; ix++)
{
listToGrid.Add(ObjectMother.GetEmployee(ix));
}
Assert.AreEqual(3, listToGrid.Count, "SortableBindingList does not have correct count");
Assert.AreEqual(3, residueGrid.Rows.Count, "DataGrid is not bound to list");
Thanks for any help you can give me.
DataGridView residueGrid = new DataGridView();
List<Employee> baseListToGrid = new List<Employee>();
SortableBindingList<Employee> listToGrid = new SortableBindingList<Employee>(baseListToGrid);
// residueGrid.DataSource = listToGrid; <-- move this line...
for (int ix = 1; ix < 4; ix++)
{
listToGrid.Add(ObjectMother.GetEmployee(ix));
}
// residueGrid.DataSource = listToGrid; <-- ...to here!
Assert.AreEqual(3, listToGrid.Count, "SortableBindingList does not have correct count");
Assert.AreEqual(3, residueGrid.Rows.Count, "DataGrid is not bound to list");
A useful structure for writing test is the following:
public void MyTest()
{
// Arrange
// Act
// Assert
}
In this case, Arrange would be instantiating all the objects, and filling the list. Act is where you set the data source of the gridview, and Assert is where you check that everything went OK. I usually write out those three comment lines each time I start writing a test.
Well, I solved the problem, and pretty much confirmed that it is something being done in the initialization of the control when added to the form that makes the DataSource binding work.
It suddenly dawned on me that that the "target" created by the MS testing framework is a private accessor to the Form itself. So I changed the line
DataGridView residueGrid = new DataGridView();
in the above code to, instead of creating a new DGV object, just reference the one on the target form:
DataGridView residueGrid = target.residueGrid;
That change made everything work as expected.

Resources