Change Text on Status Strip Label inside a click event on Table Layout Panel moves Form to Selected Control - c++

I have a very weird issue which I cannot manage to fix.
The Problem
Inside a Windows Form I have a StatusStrip and a tableLayoutPanel. That StatusStrip contains one label which will change its text depending on the row clicked. The tableLayoutPanel is set to autoScroll since the content is bigger than the size of my form.
Each row will have different controls. You can find buttons, labels, text boxes or checkboxes.
I'm able to select the row and change the text according to the selected row. The problem comes when I have scrolled down and then click on the table. For some reason, the Form will move to the last selected Control within the table.
For instance, If I have clicked inside a textbox, and I scroll down, where I cannot see the textbox, when I click on a table row. The form will automatically move to show the textbox.
It seems to me that for some reason, when I change the text it must repaint the whole table and then it moves to the last selected control.
What Have I tried
Move the table inside a panel.
Move the panel to another panel.
As for now, the panel is removed, doesn't seem to be necessary.
Originally I had 6 labels, removed them all to ensure it happens with just one.
If I don't change the text, but for instance, shows a message inside the Debug then the problem disappears, which for me it indicates that the problem resides inside the "change text" event.
** EDITED **: If I change a label inside the table, the problem is gone. So it seems to be a combination with the table + the status label.
** EDITED **: If I move the auto scroll to the form instead of the table. Then the problem is gone. But this is not what I want because then, my status bar won't be visible since it will be on the button and I need it to keep visible.
** EDITED **: If I use a menu strip the problem is still there. If I move the status Stript. The problem is still there.
** EDITED **: If I remove the panels and use a label inside the form. The problem persist.
The Code
I will keep code to a minimum and will add more if it is necessary.
This is where I update the toolStriptStatusLabel:
System::Void updateSelectedRow(int row) {
if (row == selectedRow)
return;
selectedRow = row;
Command ^cmd = commandsList[row];
commandToolStripStatusLabel->Text = cmd->name;
}
This is my click event:
System::Void tableLayoutPanel1_Click(System::Object^ sender, System::EventArgs^ e) {
System::Drawing::Point ^point = GetRowColIndex(
tableLayoutPanel1,
tableLayoutPanel1->PointToClient(Cursor->Position
));
updateSelectedRow(point->Y);
}
This is where I find the Row I clicked:
System::Drawing::Point ^GetRowColIndex(TableLayoutPanel ^tlp, Point point) {
if (point.X > tlp->Width || point.Y > tlp->Height)
return nullptr;
int w = tlp->Width;
int h = tlp->Height;
array<int> ^widths = tlp->GetColumnWidths();
int i;
for (i = widths->Length - 1; i >= 0 && point.X < w; i--)
w -= widths[i];
int col = i + 1;
array<int> ^heights = tlp->GetRowHeights();
for (i = heights->Length - 1; i >= 0 && point.Y < h; i--)
h -= heights[i];
int row = i + 1;
return gcnew Point(col, row);
}

Related

Simple relative cell referencing in google code

I have made this virtual inventory with buttons that copy and paste values to various sheets for different types of reports. The code might not be optimal I am not much of a programmer but I'm trying my best. now I have the same button in multiple places and I want it to copy values in cells relative to the position of the button itself but I am not sure how to reference a cell in a relative manner in the getRange fucntion.
here is the code:
function Go() {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var sheet = SpreadsheetApp.getActiveSheet();
var destSheet = ss.getSheetByName("Fiche");
var lastRow = destSheet.getLastRow();
var source = ss.getRange ('(rowid-3)(colid)');
var checkbox = sheet.getRange (3,3);
var destColumn = ss.getRange ('B10').getValues ();
source.copyTo(destSheet.getRange(lastRow + 1,1), {contentsOnly: true});
if (checkbox.getValue() == 'vrai' ) {
var source = ss.getRange ('B9');
source.copyTo(destSheet.getRange(lastRow + 1, destColumn), {contentsOnly: true});
source.copyTo(destSheet.getRange(lastRow + 1, 2), {contentsOnly: true});
}
else {
var nombre = ss.getRange ('D5').getValues() ;
destSheet.getRange(lastRow + 1, destColumn).setValue(nombre);
destSheet.getRange(lastRow + 1, 2).setValue(nombre)
}
I want all (B10),(B9), etc. format cells to be replaced with relative cell positons. I have tried with (colID)(rowID) but it doesn't seem to work
I believe your goal as follows.
You want to retrieve the cell coordinate when a button on Spreadsheet is clicked.
Modification points:
Unfortunately, in the current stage, when a button on Spreadsheet is clicked, there are no methods for directly retrieving the information where button is clicked.
So when you want to use the button on Spreadsheet, if you want to retrieve the information, it is required to prepare each script for each button. But I thought that this might be different from the direction you expect.
In this answer, in order to retrieve the information where the button is clicked, I would like to propose the following 2 patterns as the workarounds. In these patterns, the event object is used. By this, the information of the button can be retrieved. This is used.
Pattern 1:
In this pattern, a cell is used as the button using the simple trigger of OnSelectionChange. When the cell "B3" is used as the button like above image, the script is as follows.
Sample script:
In this script, for example, when you want to use the cell "B3" of "Sheet1" as the button, please set "B3" to buttonRanges. And, set sheet.getSheetName() != "Sheet1". When when you want to use the cell "B3" and "B4" as the button, please set "B3" and "B4" to buttonRanges.
function onSelectionChange(e) {
const range = e.range;
const sheet = range.getSheet();
const buttonRanges = ["B3"];
if (sheet.getSheetName() != "Sheet1" || !buttonRanges.includes(range.getA1Notation())) return;
const row = range.getRow(); // This is the row number.
const column = range.getColumn(); // This is the column number.
}
In this case, when the cell "B3" is clicked, this script is run. And row and column are the row and column number of the cell.
Because the cell is used as the button, you can put the value to the cell.
Pattern 2:
In this pattern, a checkbox is used as the button using the OnEdit trigger. When the cell "B3" is used as the button like above image, the script is as follows.
Sample script:
In this script, for example, when you want to use the cell "B3" of "Sheet1" as the button, please set "B3" to buttonRanges. And, set sheet.getSheetName() != "Sheet1". When when you want to use the cell "B3" and "B4" as the button, please set "B3" and "B4" to buttonRanges.
function onEdit(e) {
const range = e.range;
const sheet = range.getSheet();
const buttonRanges = ["B3"];
if (sheet.getSheetName() != "Sheet1" || !buttonRanges.includes(range.getA1Notation()) || !range.isChecked()) return;
const row = range.getRow(); // This is the row number.
const column = range.getColumn(); // This is the column number.
}
In this case, when the checkbox is checked, the script is run. When the checkbox is unchecked, the script is not run.
Because the checkbox is used as the button, you cannot see the text in the cell.
References:
Simple Triggers
Event Objects
Related question
Button change text display every after click
This question is for achieving the switching button.

MFC/C++ ComboBox: disable drawing of Dropdown closing & opening (UI freeze)

I've just added an Item-Filter-Feature to a CComboBox derived class called
ComboBoxFbp in an old MFC application.
BOOL CComboBoxFbp::OnEditChange()
{
CString csText;
if (m_wFbpMode & _FbpMode_UserTextFiltersList) {
GetWindowText(csText);
// This makes the DropDown "flicker"
// ShowDropDown(false);
// Just insert items that match
FilterItems(csText);
// Open DropDown (does nothing if already open)
ShowDropDown(true);
}
return FALSE; // Notification weiterleiten
}
void CComboBoxFbp::FilterItems(CString csFilterText)
{
CString csCurText;
int nCurItem;
DWORD wCurCursor;
// Text/selection/cursos restore
GetWindowText(csCurText);
nCurItem = GetCurSel();
if (nCurItem != CB_ERR && nCurItem >= 0 && nCurItem < GetCount()) {
CString csCurItemText;
GetLBText(nCurItem, csCurItemText);
if (csCurItemText == csCurText) csCurText = csCurItemText;
else nCurItem = CB_ERR;
} else {
nCurItem = CB_ERR;
}
wCurCursor = GetEditSel();
// Delete all items
ResetContent();
csFilterText.MakeLower();
// Add just the items (from the vector of all possibles) that fit
for (auto item : m_vItems)
{
CString csItemText = item.first;
csItemText.MakeLower();
if (!csFilterText.IsEmpty() && csItemText.Find(csFilterText) < 0)
continue;
const int i = AddString(item.first);
SetItemData(i, item.second);
}
// Text/selection/cursos restore
if (nCurItem != CB_ERR) SelectString(-1, csCurText);
else SetWindowText(csCurText);
SetEditSel(LOWORD(wCurCursor), HIWORD(wCurCursor));
}
So when the user types, the long list of items in the DropDown gets filtered accordingly. Everything's fine so far.
The size/height of the ListBox/DropDown doesn't change once its open. It does change accordingly when die DropDown opens. Meaning if there are only 2 items the DropDown is only 2 items high.
My issue
When the user enters a text where just one item fits the DropDown is only 1 item in height (this happens with some user workflows, i.e. user manually closes & opens the DropDown).
Now when the user now changes the text so multiple items are fitting the height stays 1 item and it looks weird as even the scrollbar doesn't look correct as it doesn't fit.
What I've tried so far
I cannot use CComboBox::SetMinVisibleItems (or the MSG behind it) as it only works in a Unicode CharacterSet (which I'm not able to change in this old application) and from WinVista onwards (app runs on WinXP).
The only other option is to close and open the DropDown so it gets redrawn correctly with the correct height (see // This makes the DropDown "flicker" in Source Code above).
Now going with option 2 I don't want the user to see the closing and opening ("flicker") of the DropDown after every key he is pressing.
To prevent this I've tried a couple of solutions I've found but none works in my case with a ComboBox-DropDown. Here's a list of methods I've put just before the ShowDropDown(false) and just after the ShowDropDown(true).
EnableWindow(false/true);
(Un)LockWindowUpdate();
SendMessage(WM_SETREDRAW, FALSE/TRUE, 0)
With all three calls I still see the DropDown closing/opening.
Do you guys have other ideas how I can prevent this flicker?
Thanks in advance
Soko
This is an XY question.
It should be easier to use the following approach to adjust the height of the ComboBox
Use GetComboBoxInfo to get the handle of the list control.
Use OnChildNotify or ON_CONTROL_REFLECT and capture CBN_DROPDOWN.
In the handler of the message resize the window as needed Use SetWindowPos and just change the size.

QListWidget horizontal scrollbar causes selection to go out of view

I've asked this question previously and a wonderful person lead me to a decent workaround for the issue. However, I am hoping to see if there is a better solution. One that actually prevents any shifting in my QListWidget entirely.
Working demo example
ListDemo zipfile
http://nexrem.com/test/ListDemo.zip
ListDemo cpp code
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myListWidget = new QListWidget();
/*
* The signal-slot below is a temporary workaround for the shifting issue.
* This will ensure that the item selected remains in view,
* This is achieved by forcing the item to be in the center of the window;
* however, this has an undesired side-effect of visible 'jumping' as the list
* scrolls to center the item.
*/
//connect (myListWidget, SIGNAL(itemClicked(QListWidgetItem*)), this,
// SLOT(scrollToItem(QListWidgetItem*)));
for (int i = 0; i <= 1000; ++i)
{
QListWidgetItem * myItem = new QListWidgetItem(myListWidget);
QString text("");
for (int i = 0; i <= 40; ++i)
{
text.append("W");
}
myItem->setText(text + QString::number(i));
}
for (int i = 0; i <= 1000; ++i)
{
if (i%2)
myListWidget->item(i)->setHidden(true);
}
auto selected = myListWidget->selectedItems();
if (selected.size() == 1)
{
myListWidget->scrollToItem(selected.front());
}
setCentralWidget(myListWidget);
}
void MainWindow::scrollToItem(QListWidgetItem * item)
{
std::cout << "Scrolling to the item." << std::endl;
myListWidget->scrollToItem(item, QAbstractItemView::PositionAtCenter);
}
The problem:
Whenever I have a QListWidget with horizontal scrollbars and hidden rows present, I get an undesired behaviour that whenever a user clicks on an item, it disappears from view, and the entire list shifts down.
In the example above, I've hidden every other row, to demonstrate this behaviour.
The workaround:
Workaround is having a signal-slot connection that forces the selected item to be scrolled back into view and positioned at the center.
Note, that I have to use PositionAtCenter as EnsureVisible does not work. It thinks the item is visible when it is out of view.
This workaround is acceptable; however, there is visible 'jump' when your selection is forced to be positioned at the center. This is an undesirable side effect.
At this point I am not sure whether this is a QT bug (I don't think having horizontal scrollbar should force your selection out of view) or my code is missing something vital.
The Fix:
As per #G.M.'s comment, all that was missing is myListWidget->setAutoScroll(false);
As mentioned in the comment...
To prevent automatic scrolling on selection disable the autoScroll property. So, in the example code provided do...
myListWidget->setAutoScroll(false);
Note that this property also has an effect when items are dragged over the list view so if you want your list view to act as a drop site then you will probably want to re-enable this property when you get a QDragEnterEvent.

How to get current row of QTableWidget if I clicked on its child?

I have created a QTableWidget in which I've used setCellWidget(QWidget*). I've set QLineEdit in the cell widget. I've also created a delete button and clicking that button sends a signal to the function deleteRow. I've also used a function currentRow() to get the current row, but it returns -1 because of the QLineEdit. The code snippet is below.
void createTable() {
m_table = new QTableWidget(QDialog); //member variable
for (int i = 0; i < 3; i++)
{
QLineEdit *lineEdit = new QLineEdit(m_table);
m_table->setCellWidget(i, 0, lineEdit);
}
QPushButton *deleteBut = new QPushButton(QDiaolg);
connect(deleteBut, SIGNAL(clicked()), QDialog, SLOT(editRow()));
}
editRow() {
int row = m_table->currentRow(); // This gives -1
m_table->remove(row);
}
In above scenario I click in the QLineEdit and then click on the button delete. Please help me out with a solution.
Just tried it here, it seems that currentRow of the table returns -1 when clicking the button right after program start, and when first selecting a cell, then selecting the QLineEdit and then clicking the button, the correct row is returned.
I would do the following as a workaround: Save the row number in the QLineEdit, e.g. by using QObject::setProperty:
QLineEdit *lineEdit = new QLineEdit(m_table);
lineEdit->setProperty("row", i);
m_table->setCellWidget(i, 0, lineEdit);
Then, in the editRow handler, retrieve the property by asking the QTableWidget for its focused child:
int row = m_table->currentRow();
if (row == -1) {
if (QWidget* focused = m_table->focusWidget()) {
row = focused->property("row").toInt();
}
}
The accepted solution, as is, would not work if rows might get deleted while the program runs. Thus the approach would require to update all the properties. Can be done, if this is a rare operation.
I got away with an iteration approach:
for(unsigned int i = 0; i < table->rowCount(); ++i)
{
if(table->cellWidget(i, relevantColumn) == QObject::sender())
{
return i;
}
}
return -1;
Quick, dirty, but worked, and in my case more suitable, as rows got deleted often or changed their positions, only buttons in the widget were connected to the slot and the slot was never called directly. If these conditions are not met, further checks might get necessary (if(QObject::sender()) { /* */ }, ...).
Karsten's answer will work correctly only if QLineEdit's property is recalculated each time a row is deleted, which might be a lot of work. And Aconcagua's answer works only if the method is invoked via signal/slot mechanism. In my solution, I just calculate the position of the QlineEdit which has focus (assuming all table items were set with setCellWidget):
int getCurrentRow() {
for (int i=0; i<myTable->rowCount(); i++)
for (int j=0; j<myTable->columnCount(); j++) {
if (myTable->cellWidget(i,j) == myTable->focusWidget()) {
return i;
}
}
return -1;
}

How do I make pressing the Enter key cause focus to move to the cell below like Excel's default behavior?

I am using an Infragistics UltraWinGrid v9.1.
I want to allow the user to enter numerical data in a cell, press Enter and then have the focus on the cell below, like you see in Excel. It appears that the KeyUp event might be better than the KeyPressed event for this, but I keep throwing an exception that I have gone beyond the bounds of the UltraWinGrid even though I start at the top of a full grid. Here is the code I've tried:
private void ugrid_KeyUp(object sender, KeyEventArgs e)
{
UltraGrid grid = (UltraGrid)sender;
if (e.KeyCode == Keys.Enter)
{
// Go down one row
UltraGridCell cell = grid.ActiveCell;
int currentRow = grid.ActiveRow.Index;
int col = cell.Column.Index;
grid.Rows[currentRow + 1].Cells[grid.ActiveCell].Activate();
}
}
I expected this to make the cell in the same column but one row below to become the active cell with the call,
grid.Rows[currentRow + 1].Cells[grid.ActiveCell].Activate();
Instead an exception is thrown:
An exception of type
'System.IndexOutOfRangeException'
occurred in
Infragistics2.Shared.v9.1.dll but was
not handled in user code Additional
information: Index was outside the
bounds of the array.
Since I'm on row 0 and there exists a row 1 this is a surprise to me. The values for currentRow and col are 0 and 28 respectively. What would be a better approach?
btw I can do this again the cell below, where the values are currentRow = 1 and col = 28. The same exception is thrown.
Someone answered my question on the Infragistics fora...
private void ugrid_KeyUp(object sender, KeyEventArgs e)
{
var grid = (UltraGrid)sender;
if (e.KeyCode == Keys.Enter)
{
// Go down one row
grid.PerformAction(UltraGridAction.BelowCell);
}
}
I don't know if what I'm saying is valid also for the v9.1 but you can also do something like this:
yourGrid.KeyActionMappings.Add(new GridKeyActionMapping(Keys.Enter, UltraGridAction.BelowCell, 0, UltraGridState.Row, SpecialKeys.All, 0));
private void ulGrvProducts_KeyUp(object sender, KeyEventArgs e)
{
UltraGrid grid = (UltraGrid)sender;
if (e.KeyCode == Keys.Enter)
{
//Go down one row
grid.PerformAction(UltraGridAction.BelowCell);
}
}
After using grid.PerformAction(UltraGridAction.BelowCell) , active row will change but next cell is not be in edit mode.