vb.net set cell numeric format after cellendedit event - vb.net

I have predefined columns in my DataGridView.
There are two editable columns and one read-only column.
I have set all columns format to numeric style from cellstyle builder
When I input some text into the two columns, the format won't change, but the read-only column do.
So I've added format change on cellformatting event like this
Dim c As String
If e.ColumnIndex = colHrg.Index _
AndAlso e.Value IsNot Nothing AndAlso e.RowIndex >= 0 Then
c = e.Value.ToString
e.Value = c
End If
e.CellStyle.Format = "N3"
But it seems the event is not fired, so I decided to add format styling on cellendedit event.
Yet, it still didn't worked
dgv_beli.Columns("colHrg").DefaultCellStyle.Format = "N3"
I've also tried editingcontrolshowing and cellvaluechanged event, but still not working
Can someone guide me how to fixed this. I've spent a lot of time to solved this, really curious to find a good solution for my problem.

Add an event of EditingControlShowing
In EditingControlShowing, check that if the current cell lies in the desired column.
Register a new event of KeyPress in EditingControlShowing(if above condition is true).
Remove any KeyPress event added previously in EditingControlShowing.
In KeyPress event, check that if key is not digit then cancel the input.
Example:
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
e.Control.KeyPress -= new KeyPressEventHandler(Column1_KeyPress);
if (dataGridView1.CurrentCell.ColumnIndex == 0) //Desired Column
{
TextBox tb = e.Control as TextBox;
if (tb != null)
{
tb.KeyPress += new KeyPressEventHandler(Column1_KeyPress);
}
}
}
private void Column1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
{
e.Handled = true;
}
}

Related

Winforms combobox bug - 2 items with the same value but different key

Is this truly a bug in Winforms in 2015 or am I just doing something wrong...
1) Create a new winforms project (.net 4.0) and add a combobox to the main form.
2) Use this for your form load code:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim items As New Dictionary(Of Integer, String)
items.Add(1, "Value 1")
items.Add(2, "Value 2")
items.Add(3, "Value 3")
items.Add(4, "Value 3")
Dim dataSource As New BindingSource(items, Nothing)
ComboBox1.DataSource = dataSource
ComboBox1.DisplayMember = "Value"
ComboBox1.ValueMember = "Key"
End Sub
Notice how items 3 & 4 have the same value, but different keys and that the display and value members are set correctly (unless I am going crazy, which is possible). When you run the application, open the combobox and select the last item. Now, open the combobox back up and you will notice that the 2nd to last item is now selected. That is a problem.
Any thoughts?
Thanks!
EDIT: I added an Infragistics UltraComboEditor to the form and placed the following code in the form load event:
For Each item As KeyValuePair(Of Integer, String) In items
UltraComboEditor1.Items.Add(New ValueListItem With {.DataValue = item.Key, .DisplayText = item.Value})
Next
UltraComboEditor1.SelectedIndex = 0
UltraComboEditor1.AutoComplete = True
The Infragistics control allows me to autocomplete and enter my own text and it is not changing my selection when I select an item with the same text as the item above it. The Winforms control should not be changing my selection like that.
When the ComboBox allows the text portion to be edited, then it will pattern match and highlight the first prefix text that matches. This has the side effect that when the listbox is closed, the selected item is updated.
When the ComboBox's DropDownStyle == DropDownList mode, then the item previously selected will be highlighted in the dropdown list.
You can change the behavior by assigning a NativeWindow to the list window and then listen for the LB_SETCURSEL Msg.
You can use this thread as a starting point: Prevent AutoSelect behavior of a System.Window.Forms.ComboBox (C#)
Add an int index field to the Data object. Then in the Register method add:
combo.SelectedIndexChanged += delegate {
data.index = combo.SelectedIndex;
};
Then pass the Data to the native window, which keeps track of the previously selected index.
private class NW : NativeWindow {
Data data;
public NW(IntPtr handle, Data data) {
this.AssignHandle(handle);
this.data = data;
}
private const int LB_FINDSTRING = 0x018F;
private const int LB_FINDSTRINGEXACT = 0x01A2;
private const int LB_SETCURSEL = 0x0186;
protected override void WndProc(ref Message m) {
if (m.Msg == LB_FINDSTRING)
m.Msg = LB_FINDSTRINGEXACT;
if (m.Msg == LB_SETCURSEL)
m.WParam = (IntPtr) data.index;
base.WndProc(ref m);
}
}

Datagridview Cancel Drag and Drop on Filler Column

I have a datagridview with the AllowUserToOrderColumns property set to true.
I also have a column at the end of the grid which has AutoSizeMode set to fill, this serves to fill the grid so that rows look "complete"
How would I mark this column to disable reordering on this column only?
I have tried something like the following
Private Sub dgvAdjustments_ColumnDisplayIndexChanged(sender As Object, e As DataGridViewColumnEventArgs) Handles dgvAdjustments.ColumnDisplayIndexChanged
If e.Column Is TheFillerColumn Then
e.Column.DisplayIndex = theGrid.Columns.Count - 1
End If
End Sub
Which this post How to get dragged column in datagridview when AllowUserToOrderColumns = true
sort of suggested
However trying to do what I want to do in this event throws an error as you can't set the display index on a column that is being adjusted.
Regards.
You can use the SynchronisationContext's Post method.
In C#, you will have for example :
private void dataGridView1_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e)
{
SynchronizationContext.Current.Post(delegate(object o)
{
if (Column1.DisplayIndex != 1)
Column1.DisplayIndex = 1;
}, null);
}
Also, don't do e.Column.DisplayIndex = ... but TheFillerColumn.DisplayIndex = ... instead, as the moved column can be another one and be moved after TheFillerColumn.

Unbound DataGridView : Need ComboBox In cell of 2nd column only when user clicks cell

I have a unbound DataGridView with two columns. First column is just string values.
Second column I want to display a combobox, only when user click the cell(not the whole column as DataGridViewColumn). I use the below code which is incorrect and gives me the error : Operation is not valid because it results in a reentrant call to the SetCurrentCellAddressCore function.
The first column is popuated, and the second column is empty.
The code is as below :
Private Sub DGVFieldsMap_CellEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DGVFieldsMap.CellEnter
If e.ColumnIndex = 1 Then
If cboClmCell Is Nothing Then
Dim dgv As DataGridView = CType(sender, DataGridView)
cboClmCell = New DataGridViewComboBoxCell
cboClmCell.Items.Add("A")
cboClmCell.Items.Add("B")
cboClmCell.Items.Add("C")
cboClmCell.Items.Add("D")
cboClmCell.Items.Add("E")
cboClmCell.Items.Add("F")
dgv.Focus()
dgv(e.ColumnIndex, e.RowIndex) = cboClmCell '[Error Here]
isCombo = True
End If
End If
End Sub
Private Sub DGVFieldsMap_CellValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellValidatingEventArgs) Handles DGVFieldsMap.CellValidating
If e.ColumnIndex = 1 Then
Dim dgv As DataGridView = CType(sender, DataGridView)
If isCombo Then
isCombo = False
cboClmCell = Nothing
dgv(e.ColumnIndex, e.RowIndex) = New DataGridViewTextBoxCell()
End If
End If
End Sub
Can anybody give me a complete working example with two columns, the second column being a ComboBoxCell, but only when user clicks. Also I need to get the selected values in the DataGridView cell. Thanks In Advance.
Don't try to replace the columns in event handlers, instead create a DataGridView with 2 columns, have the 2nd column be your DataGridViewComboBoxColumn. There is a property on that column called "DisplayStyle" which determines how the column looks when not editing. Set it to "Nothing". Now it will look like a textbox until you go into edit mode at which point it looks like a combobox.
I have a similar DataGridView where the first column is a textual label and the second column is ComboBox.
Note: The code below is in C#, but the concept is the same as in vb.net
In the form's load event, call a function that sets up the datasource and creates the columns
private void frmCfgEdit_Load(object sender, EventArgs e)
{
// Fill CFG Data Grid
FillCfgDataGrid();
}
private void FillCfgDataGrid()
{
// Do not automatically generate the columns based on the datasource public fields
dgCFG.AutoGenerateColumns = false;
// Define data source
dgCFG.DataSource = _pDriveElement.CfgTableViewRecs;
// Define data grid columns
SetUpCFGDataGrid(dgCFG);
}
public void SetUpCFGDataGrid(DataGridView dgCFG, String TableIdentifier)
{
// Create datagridview text column
AddGridColumn(dgCFG, "Label", "CfgLabel", 350, typeof(System.String), true, false);
// Create datadridview combobox column
AddGridComboColumn(dgCFG, "Value", 350, typeof(System.String), false, true);
}
public void AddGridColumn(DataGridView dg, String sHeaderText, String sDataPropertyName, int iWidth, Type tyValueType, bool bReadOnly, bool bLastCol)
{
DataGridViewTextBoxColumn colTxt = new DataGridViewTextBoxColumn();
colTxt.HeaderText = sHeaderText;
colTxt.Width = iWidth;
colTxt.ReadOnly = bReadOnly;
// Add the text box to the data grid
dg.Columns.Add(colTxt);
int iColumn = dg.Columns.Count - 1;
// Define bindings to text columns
dg.Columns[iColumn].DataPropertyName = sDataPropertyName;
dg.Columns[iColumn].ValueType = tyValueType;
if (tyValueType == typeof(System.Single)) dg.Columns[iColumn].DefaultCellStyle.Format = "F6";
if (bLastCol) dg.Columns[iColumn].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
if (iColumn > 0) dg.Columns[iColumn].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
}
public void AddGridComboColumn(DataGridView dg, String sHeaderText, int iWidth, Type tyValueType, bool bReadOnly, bool bLastCol)
{
DataGridViewComboBoxColumn cb = new DataGridViewComboBoxColumn();
cb.FlatStyle = FlatStyle.Flat;
cb.HeaderText = sHeaderText;
cb.Width = iWidth;
cb.ReadOnly = bReadOnly;
dg.Columns.Add(cb);
int iColumn = dg.Columns.Count - 1;
// Combo box is always left aligned
dg.Columns[iColumn].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;
if (bLastCol) dg.Columns[iColumn].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}

Display `00` instead of `0` in a NumericUpDown control

I'm letting users select a date/time for a scheduled task to run, using two NumericUpDowncontrols.
I'd like one-digit values to be padded with a leading 0, so as to display 09:00 instead of 9:0.
The definitive solution is to use a DateTimePickerwith ShowUpDown set to True and Format set to Time or Custom. In the latter case, you'd use hh:mm or HH:mm as a custom format.
class CustomNumericUpDown:System.Windows.Forms.NumericUpDown
{
protected override void OnTextBoxTextChanged(object source, EventArgs e)
{
TextBox tb = source as TextBox;
int val = 0;
if (int.TryParse(tb.Text,out val))
{
if (val < 10)
{
tb.Text = "0" + val.ToString();
}
}
else
{
base.OnTextBoxTextChanged(source, e);
}
}
}
I had to do this this morning and came up with a Customised Numeric Up Down for my Windows Forms application. You should be able to change this easily enough to VB.NET.
This is not possible with a NumericUpDown Control.
I have a clever idea~
Why don't you put a textbox covering the textbox part of the numericupdown control (only the scroll of numericupdown will be shown)?
Set your textbox with "00" as the initial value, then disable it, so that the user can't control your textbox.
Then type these codes:
Private Sub numericupdown1_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ndFrom.ValueChanged
If numericupdown1.Value < 10 Then
textbox1.Text = "0" & numericupdown1.Value
Else
textbox1.Text = numericupdown1.Value
End If
End Sub
class MyNumericUpDown : System.Windows.Forms.NumericUpDown
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (value.Length < 2)
value = "0" + value;
base.Text = value;
}
}
}

Data confusion - Selecting data in one DataGridView based on selection in another

This probably isn't the best way to do what I want, but I can't think of anything else to try...
NB: I'm using Visual Basic.NET
My form has 2 DataGridView controls. One of these is bound to a DataSet, the other isn't visible - at least not until the user selects a uniqueidentifier cell in the 1st grid.
When the user makes this selection, the 2nd grid will become visible and display the row from another with the same id as the one selected in the 1st grid.
So, basically, I want to dynamically display data in one grid based on user selection in another grid.
My code looks like this so far...
Private Sub RulesGrid_CellClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles RulesGrid.CellClick
Try
FlagsGrid.Visible = True
''// MsgBox(RulesGrid.CurrentCell.Value.ToString())
Dim sql As String = "SELECT * FROM ctblMKA_Status_Flags " + _
"WHERE intStatusID = '" & RulesGrid.CurrentCell.Value & "'"
DSFlags = GetDS(sql)
DSFlags.DataSetName = "FlagsDataSet"
FlagsGrid.DataSource = DSFlags
ProgressBar.Visible = False
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
I feel like I'm missing something here... Any ideas anyone?
Use a seperate DataView for the second DataGrid.
If you are using a DataTable, assign a CurrencyManager, then add PositionChanged on your CurrencyManager variable.
class-level variable:
CurrencyManager _cmShoe;
after loading data to a datatable:
// make a currency(current) manager on DataTable variable
_cmShoe = this.BindingContext[_dtShoe] as CurrencyManager;
// add event on the CurrencyManager variable
_cmShoe.PositionChanged += cmShoe_PositionChanged;
the event:
void cmShoe_PositionChanged(object sender, EventArgs e)
{
Guid shoeGuid = (Guid)((cmShoe.Current as DataRowView)["shoe_id"]);
// _dtShoeMaterial is the table of the second datagridview
_dtShoeMaterial.DefaultView.RowFilter = "shoe_id = '" + shoeGuid.ToString() + "'";
}
If you are using BindingSource(instead of DataTable), just add a PositionChanged event on the BindingSource variable, and use this event:
void cmShoe_PositionChanged(object sender, EventArgs e)
{
Guid shoeGuid = (Guid)((bdsShoe.Current as DataRowView)["shoe_id"]);
// bdsShoeMaterial is the table of the second datagridview
bdsShoeMaterial.Filter = "shoe_id = '" + shoeGuid.ToString() + "'";
}