DataGridView problem when adding columns - windows

Hey guys! First time I use Visual Studio 2008 Windows Form Application with C++.
I want to put a dataGridView on my form. That is ok. But when adding one column, on the gridView appear 2 columns. First with no header (which I dont want) and the second is ok(my column). I tried to set the column number from code, but the same thing happens.
Any idea to solve this?
Thanks,
Andrew
//
// table
//
this->table->AllowUserToAddRows = false;
this->table->AllowUserToDeleteRows = false;
this->table->ColumnHeadersHeightSizeMode = System::Windows::Forms::DataGridViewColumnHeadersHeightSizeMode::AutoSize;
this->table->Columns->AddRange(gcnew cli::array< System::Windows::Forms::DataGridViewColumn^ >(1) {this->Column1});
this->table->Location = System::Drawing::Point(13, 89);
this->table->Name = L"table";
this->table->ReadOnly = true;
this->table->Size = System::Drawing::Size(240, 150);
this->table->TabIndex = 2;
//
// Column1
//
this->Column1->HeaderText = L"Column1";
this->Column1->Name = L"Column1";
this->Column1->ReadOnly = true;

Are you adding the column in the constructor?
I've had a problem in the past where items added in the constructor get added twice, because (I think) the constructor gets called to make the control appear in the designer view, (which then gets added to the instance) and then again when you run your program.
If that is what's happening for you, you need to check that you aren't in design mode.
There are a few different ways to check this, depending on context - see How to tell if .NET code is being run by Visual Studio designer

Related

Windows Forms c++ cannot set default values in datagrid cells

I've just started working with Windows Forms on Visual Studio 2019. I am most familiar with C++ and thus have been creating an application with C++/CLI. The basis of the project is that I have a schedule generating set of functions these are called in MyForm.h which is the main form originally launched. MyForm.h also opens MyForm2.h when a button is pressed. This MyForm2.h is where I have a problem. I want to allow the user to edit a series of specific settings for the schedule generator. The most user friendly way I've found to do this is with a datagrid using textboxes and checkmarks, but I need to have the original timeslots for the schedule in the datagrid for the user to edit when the open MyForm2.h.
I've looked all over to find how to set default values for a datagrid, but have only come up with two ok methods, both of which have some fatal flaws.
The first was from this microsoft guide.
The problem here is that the solution is written in c#, and I don't know how to implement it in a c++ program.
The second was from a few sites which generally displayed one of two formats which are displayed here in a way that is formatted to how I used them in my forms:
void InitializeComponent()
{
this->dgvStudents = (gcnew System::Windows::Forms::DataGridView());
this->dataGridViewTextBoxColumn1 = (gcnew System::Windows::Forms::DataGridViewTextBoxColumn());
(cli::safe_cast<System::ComponentModel::ISupportInitialize^>(this->dgvStudents))->BeginInit();
this->SuspendLayout();
this->dgvStudents->Columns->Add(this->dataGridViewTextBoxColumn1);
this->dgvStudents->Location = System::Drawing::Point(0, 0);
this->dgvStudents->Name = L"dgvStudents";
this->dgvStudents->Size = System::Drawing::Size(350, 100);
this->dgvStudents->TabIndex = 0;
//
// dataGridViewTextBoxColumn1
//
this->dataGridViewTextBoxColumn1->HeaderText = L"First Name";
this->dataGridViewTextBoxColumn1->Name = L"dataGridViewTextBoxColumn1";
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(638, 261);
this->Controls->Add(this->dgvStudents);
this->Name = L"MyForm3";
this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
this->Text = L"Exercise";
(cli::safe_cast<System::ComponentModel::ISupportInitialize^>(this->dgvStudents))->EndInit();
this->ResumeLayout(false);
// Option 1
this->dgvStudents->Rows[0]->Cells[0]->Value = "MLP";
// Option 2
this->dgvStudents->Rows->Add("MLP");
}
I have tried putting both options in several various parts of the InitializeComponent() function and it makes no difference. The problem with Option 2 of this second solution is that it will be removed from the code if I change anything in the [Design] part of the MyForm2.h code editor in VS and save it. Weirdly, Option 1 will not be displayed in the [Design] part of the code and instead I will get the error Member Cells not found in class System.Windows.Forms.DataGridViewRowCollection, but if I launch the debugger for the program, both options will be displayed exactly how I want them to be in the GUIs that appear. As previously meantioned though, both options will most likely be removed if I change anything else in the form designer. Any help would be greatly appreciated:)

How can I change the Font of a TListBox's items?

I'm building an app with RAD Studio 11, but I can't find a way to change the item font of my TListBox.
I tried to change TListBox.Font in the Object Inspector, but when I select my TListBox called ingredientsDataBase in the Object Inspector, I can just change TListBox settings instead of TListBox.Items settings.
I add a TListBoxItem manually as follow:
Then, I can change ListBoxItem1.Font in the Object Inspector, after selecting my ListBoxItem1 (no problem).
The problem is that, when I run my program, the Font change only affects my ListBoxItem1, and I want the same Font for every item that I add to my TListBox.
UPDATE 1
After your help I tried to convert your Delphi code to C++.
__fastcall TIngredientCreator::addButtonClick(TObject *Sender){
//More code Here
//Then I ADD a new ListBoxItem to my ListBox "ingredientsDataBase"
ingredientsDataBase->Items->Add("newIngredient");
TListBoxItem *lbItem = new TListBoxItem(ingredientsDataBase);
lbItem->Parent = ingredientsDataBase;
// Remove Family and Size from the items TStyledSettings
lbItem->StyledSettings = lbItem->StyledSettings << TStyledSetting::Family << TStyledSetting::Size;
// You can now set these TextSettings as needed
lbItem->TextSettings->Font->Family = "Algerian";
lbItem->TextSettings->Font->Size = 18;
lbItem->Text = "algerian";
delete lbItem;
}
There is no syntax error, but I can't associate my new ListBoxItem, in this case the Text or Name of that new ListBoxItem called "newIngredient" (I don't know how to do it in this code), so when I run my program nothing happen to mi new Item at least.
UPDATE 2
I found a way to associate my newIngredient to TListBoxItem Object as follow:
int index = ingredientsDataBase->Items->IndexOf(newIngredient);
lbItem = ingredientsDataBase->ItemByIndex(index);
When I run the code and I add a new Ingredient, just the Text of the newIngredient is changed to "algerian", because in the first code I have this line lbItem->Text = "algerian" all good here. But Font and Size still without change.
Thanks for your answers
When you add items to the listbox, you need to clear some items from the default StyledSettings property of the new item, if you want to modify the corresponding TextSettings.
Here's an example in Delphi to do what you want:
procedure TForm5.Button2Click(Sender: TObject);
var
lbItem: TListBoxItem;
begin
lbItem := TListBoxItem.Create(ListBox1);
lbItem.Parent := ListBox1;
// Remove Family and Size from the items TStyledSettings
lbItem.StyledSettings := lbItem.StyledSettings - [TStyledSetting.Family,TStyledSetting.Size];
// You can now set these TextSettings as needed
lbItem.TextSettings.Font.Family := 'Algerian';
lbItem.TextSettings.Font.Size := 18;
lbItem.Text := 'algerian';
// In Embarcadero C++Builder you use the ">>" operator to remove members from a set, and "<<" to include them.
end;
After your help my code in a Multi-Device Application C++ Builder project result on the next code:
__fastcall TIngredientCreator::addButtonClick(TObject *Sender){
//More code Here
//Then I ADD a new ListBoxItem to my ListBox "ingredientsDataBase"
ingredientsDataBase->Items->Add("newIngredient");
int index = ingredientsDataBase->Items->IndexOf(newIngredient);
lbItem = ingredientsDataBase->ItemByIndex(index);
// Remove Family and Size from the items TStyledSettings
lbItem->StyledSettings = lbItem->StyledSettings >> TStyledSetting::Family >> TStyledSetting::Size;
// You can now set these TextSettings as needed
lbItem->TextSettings->Font->Family = "Consolas";
lbItem->TextSettings->Font->Size = 18;
delete lbItem;
}
If you are trying to do it in a Windows VCL Aplication C++ Builder Project is easier (You can change the font of the entire TListBox once):
ingredientsDataBase->Font->Size = 8.5;
ingredientsDataBase->Font->Name = "Consolas";

Check programmatically if a value is compliant with a datavalidation rule

I am using data validation rules on a Google Spreadsheet.
In my scenario, I need users to entry only valid values. I use the 'Reject input' to force them to write only validated content.
However, the 'Reject input' option works for manually entried data only, but it does not work if the user pastes content into the cell from a different source (e.g. a MS Excel document). In that case, a warning is still shown but the invalid value is written.
In other words, I need the 'Reject input' option to work also with pasted content.
OR... another approach would be to programmatically check the validity of the value according the Datavalidation rule for that cell.
Any ideas?
Thank you in advance.
I had a little play with this.
I had inconsistent behavior from google.
On occasion when I ctrl-c and ctrl-p, the target cell lost its data validation!
To do this programmatically
Write myfunction(e)
Set it to run when the spreadsheet is edited, do this by Resources>Current Project's Triggers
Query e to see what has happened.
Use the following to gather parameters
var sheet = e.source.getActiveSheet();
var sheetname = sheet.getSheetName();
var a_range = sheet.getActiveRange();
var activecell = e.source.getActiveCell();
var col = activecell.getColumn();
var row = activecell.getRow();
You may wish to check a_range to make sure they have not copied and pasted multiple cells.
Find out if the edit happened in an area that you have data validated;
if (sheetname == "mySheet") {
// checking they have not just cleared the cell
if (col == # && activecell.getValue() != "") {
THIS IS WHERE YOU CHECK THE activecell.getValue() AGAINST YOUR DATA VALIDATION
AND USE
activecell.setValue("") ;
to clear the cell if you want to reject the value
}
}
The obvious problem with this is that it is essentially repeating programmatically what the data validation should already be doing.
So you have to keep two sets of validation rules synchronized. You could just delete the in sheet data validation but I find that useful for providing the user feedback. Also is the data validation you are using provides content it is practical to leave it in place.
It would be great if there was a way of detecting that ctrl-p had been used or one of the paste-special options and only run the script in those cases. I would really like to know the answer to that. Can't find anything to help you there.
Note also that if someone inserts a row, this will not trigger any data validation and the onEdit() trigger will not detect it. It only works when the sheet is edited and by this I think it means there is a content change.
onChange() should detect insertion, it is described as;
Specifies a trigger that will fire when the spreadsheet's content or
structure is changed.
I am posting another answer because this is a programmatic solution.
It has a lot of problems and is pretty slow but I am demonstrating the process not the efficiency of the code.
It is slow. It will be possible to make this run faster.
It assumes that a single cell is pasted.
It does not cater for inserting of rows or columns.
This is what I noticed
The onEdit(event) has certain properties that are accessible. I could not be sure I got a full listing and one would be appreciated. Look at the Spreadsheet Edit Events section here.
The property of interest is "e.value".
I noticed that if you typed into a cell e.value = "value types" but if you pasted or Paste->special then e.value = undefined. This is also true for if you delete a cell content, I am not sure of other cases.
This is a solution
Here is a spreadsheet and script that detects if the user has typed, pasted or deleted into a specific cell. It also detects a user select from Data validation.
Type, paste or delete into the gold cell C3 or select the dropdown green cell C4.
You will need to request access, if you can't wait just copy & paste the code, set a trigger and play with it.
Example
Code
Set the trigger onEdit() to call this or rename it to onEdit(event)
You can attach it to a blank sheet and it will write to cells(5,3) and (6,3).
function detectPaste(event) {
var sheet = event.source.getActiveSheet();
var input_type =" ";
if (event.value == undefined) { // paste has occured
var activecell = event.source.getActiveCell();
if (activecell.getValue() == "") { // either delete or paste of empty cell
sheet.getRange(5,3).setValue("What a good User you are!");
sheet.getRange(6,3).setValue(event.value);
input_type = "delete"
}
else {
activecell.setValue("");
sheet.getRange(5,3).setValue("You pasted so I removed it");
sheet.getRange(6,3).setValue(event.value);
input_type = "paste";
}
}
else { // valid input
sheet.getRange(5,3).setValue("What a good User you are!");
sheet.getRange(6,3).setValue(event.value);
input_type = "type or select";
}
SpreadsheetApp.getActiveSpreadsheet().toast('Input type = ' + input_type, 'User Input detected ', 3);
}

Programatically updating underlying data in Slickgrid

I have 6 textboxes at the top of the screen that update an entire column(one textbox per column) based on any changes. I was selecting the columns based on their class (.l#). Here is the code (issues to follow):
function UpdateField() {
var ctrl = this;
var id = parseInt(ctrl.id.replace("item", ""), 10) - 1;
var bound = [".l1", ".l7", ".l8", ".l9"];
var fields = $(bound[id]);
for (var i = 0; i < fields.length; i++)
{
fields[i].innerHTML = $(ctrl).val();
}
};
which is bound to the keyup event for the text areas. Issues are:
1) initially fields.length was -1 as I didn't want to put data in the "add new
row" section at the bottom. However, when running it, I noticed the
final "real" record wasn't being populated. Also, when stepping through, I
noticed that the "new row" field was before the "last row" field.
2) when doing it this way, it is purely superficial: if I double click the field,
the real data hasn't been changed.
so in the grand scheme of things, I know that I was doing it wrong. I'm assuming it involves updating the data and then forcing a render, but I'm not certain.
Figured out how to do it. Modified the original code this way:
function UpdateField() {
var ctrl = this;
var id = parseInt(ctrl.id.replace("item", ""), 10) - 1;
var bound = ['title1', 'title2', 'title3', 'title4'];
var field = bound[id];
for (var i = 0; i < dataView.getLength(); i++)
{
var item = dataView.getItem(i);
item[field] = $(ctrl).val();
dataView.updateItem(i, item);
}
grid.invalidate();
};
I have 6 textboxes (item1-item6) that "bind" to fields in the sense that if I change data in a textbox, it updates all of the rows and any new rows added also have this data.
Parts where the two issues can be explained this way:
1) to work around that, though still it would be a presentational fix and not a real updating of the underlying data, one could force it to ignore if it had the active class attached. Extra work, and not in the "real" direction one is going for (masking the field).
2) It was pretty obvious with the original implementation (though it was all I could figure out via Chrome Dev Tools that I could modify at the time) that it was merely updating a div's content and not actually interacting with the data underneath. Would look nice, and perhaps one could just pull data from the item1-item6 boxes in place of the column if it is submitted, but if someone attempts to modify the cell, they'll be looking at the real data again.

ActiveReports as a convert to pdf machine

The company I'm with is likely to obtain an ActiveReports 7 license. There's a new project requirement that several webgrids (not actually webgrids, but more like html rendered with zurb) need to be converted into pdfs. At one point in the code behind they're effectively datasets or can be created into such. Is there a way to shuttle the data from the datasets into active reports, then render it out as a PDF. I'd like to keep the report as generic as possible, and thus have one active report for all the datatables, so doing using active reports as its usually done is kind of out of the question.
The only thing I can think of at the moment is a single textbox in the group header into which I could concatenate all the headers, and a single textbox in the details into which I could throw all the data for each row. The problem here is that I'd run into many formatting issues as nothing would line up properly - as tab delimiting would solve nothing here. I could have multiple textboxes with various spacing, but then it would eventually devolve into a different report for each dataset. Is it possible to apply some sort of markup so that I could keep the spacing of columns as I feed the data in. Do active reports richtextboxes honor html markup? Or is there another solution altogether?
I'd use Itextsharp, but its not free for commercial products.
Thanks,
Sam
You can dynamically build a report that will output a simple table based on a specified DataSet, well actually a System.Data.DataTable. Basically for each column in the DataTable, add a textbox to the header to hold the name of the column and add another textbox to the Detail section to hold the value.
For the textbox in the detail section set its DataField property to the name of the column. With the binding in place, you can set the report's DataSource property to the DataTable and then run the report and export it to PDF.
The following code is a basic example:
var left = 0f;
var width = 1f;
var height = .25f;
var space = .25f;
var rpt = new SectionReport();
rpt.Sections.Add(SectionType.ReportHeader, "rh").Height = height;
rpt.Sections.Add(SectionType.Detail, "detail").Height = height;
rpt.Sections.Add(SectionType.ReportFooter, "rf").Height = height;
foreach (System.Data.DataColumn col in dataTable.Columns)
{
var txt = new TextBox { Location = new PointF(left, 0), Size = new SizeF(width, height) };
txt.Text = col.ColumnName;
rpt.Sections["rh"].Controls.Add(txt);
txt = new TextBox { Location = new PointF(left, 0), Size = new SizeF(width, height) };
txt.DataField = col.ColumnName;
rpt.Sections["detail"].Controls.Add(txt);
left += width + space;
}
rpt.DataSource = dataTable;
rpt.Run();
var pdf = new PdfExport();
pdf.Export(rpt.Document, #"c:\Users\scott\downloads\test.pdf");

Resources