I've got a Windows Forms application in which I have a number of RadioButtons. These RadioButtons are placed within a FlowLayoutPanel which automatically arranges them for me. All RadioButtons that are directly added to the FlowLayoutPanel are grouped, meaning I can select only one of them. However, some of these RadioButtons are paired up with a TextBox so I can supply some argument there. But to have all this arranged properly, I add a Panel control to the FlowLayoutPanel so I can control the alignment of the RadioButton and TextBox relatively to each other myself.
These RadioButtons now have their own respective Panels as parent controls and thus are no longer included in the radio group with the other RadioButtons. I read that the the RadioButtons that are in the System.Web.UI namespace have a GroupName property, but unfortunately their System.Windows.Forms counterparts lack this property. Is there some other way I can group these radio buttons are am I going to have to handle onClick events myself?
Thanks,
Jerry
I'm afraid you'll have to handle this manually... It's not so bad actually, you can probably just store all the RadioButton in a list, and use a single event handler for all of them:
private List<RadioButton> _radioButtonGroup = new List<RadioButton>();
private void radioButton_CheckedChanged(object sender, EventArgs e)
{
RadioButton rb = (RadioButton)sender;
if (rb.Checked)
{
foreach(RadioButton other in _radioButtonGroup)
{
if (other == rb)
{
continue;
}
other.Checked = false;
}
}
}
I agree with #JonH - using tags is the cleanest way to do that (imho)
private void FormLoad(object sender, EventArgs e)
{
radioCsv.Tag = DataTargetTypes.CsvFile;
radioTabbed.Tag = DataTargetTypes.TxtFile;
radioSas.Tag = DataTargetTypes.SasFile;
}
private void RadioButtonCheckedChanged(object sender, EventArgs e)
{
var radio = (RadioButton) sender;
this.DataDestinationType = (DataTargetTypes)radio.Tag;
}
Here is a little improvement over the first answer: create a RadioGroup class that encapsulates the grouping functionality and adds support for standard keyboard navigation (up/down keys) and makes tabbing work.
To use it, simply declare a RadioGroup member in your form and new it (after InitializeComponent()), passing all the radio buttons you want in the group in proper order.
public class RadioGroup
{
List<RadioButton> _radioButtons;
public RadioGroup(params RadioButton[] radioButtons)
{
_radioButtons = new List<RadioButton>(radioButtons);
foreach (RadioButton radioButton in _radioButtons)
{
radioButton.TabStop = false;
radioButton.KeyUp += new KeyEventHandler(radioButton_KeyUp);
radioButton.CheckedChanged += new EventHandler(radioButton_CheckedChanged);
}
_radioButtons[0].TabStop = true;
}
void radioButton_KeyUp(object sender, KeyEventArgs e)
{
e.Handled = true;
RadioButton radioButton = (RadioButton)sender;
int index = _radioButtons.IndexOf(radioButton);
if (e.KeyCode == Keys.Down)
{
index++;
if (index >= _radioButtons.Count)
{
index = 0;
}
e.Handled = true;
}
else if (e.KeyCode == Keys.Up)
{
index--;
if (index < 0)
{
index = _radioButtons.Count - 1;
}
e.Handled = true;
}
radioButton = _radioButtons[index];
radioButton.Focus();
radioButton.Select();
}
void radioButton_CheckedChanged(object sender, EventArgs e)
{
RadioButton currentRadioButton = (RadioButton)sender;
if (currentRadioButton.Checked)
{
foreach (RadioButton radioButton in _radioButtons)
{
if (!radioButton.Equals(currentRadioButton))
{
radioButton.Checked = false;
}
}
}
}
}
One caveat: the up/down keys won't work well with the existing RadioButton class because it already handles the up/down keys. One easy way to fix it to subclass RadioButton and turn off handling of up/down keys:
public class RadioButtonEx : RadioButton
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Up || keyData == Keys.Down)
{
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
#Jerry, I'm not too familiar with Windows Forms, but I will take a shot. If there a property called Tag, you could tag each radio button with a unique tag.
Related
I'm trying to create a design form in visual studio with 4 checkboxes and I really want to make the user to check only one of them, and if he's not checked one, when he will press a button, he should receive a notification with the obligation to select a box, and the program should not starting.
RadioGroup is a control very similar in appearance to CheckBox. It's used to select only one RadioGroup in each group. You can define groups of radio buttons puttin inside a container (a Form, a Panel, a GroupBox). Add 4 radio buttons to your form, set the Text property.
You can check if a radio button is selected:
var isChecked = radioButton1.Checked;
Or make a method like this:
private int GetSelectedRadioIndex()
{
var buttons = new[]
{
this.radioButton1,
this.radioButton2,
this.radioButton3,
this.radioButton4
};
for (int i = 0; i < buttons.Length; i++)
{
if (buttons[i].Checked)
{
return i;
}
}
return -1;
}
If you get a <0 index, there aren't a radio selected. In other case you have a 0 index of the radio that is selected.
As indicated, use a container like a Panel or GroupBox while with a GroupBox you can set a caption to indicate what the RadioButtons are for.
Create a private list in the form
private List<RadioButton> _radioButtons;
Subscribe to the Form's OnShown event, add the following code where OptionsGroupBox is a GroupBox with four Radio Buttons. This ensures no default selection which is optional.
private void OnShown(object sender, EventArgs e)
{
_radioButtons = OptionsGroupBox.Controls.OfType<RadioButton>().ToList();
_radioButtons.ForEach(rb => rb.Checked = false);
}
Add a button to assert/get their selection.
private void CheckSelectionButton_Click(object sender, EventArgs e)
{
var selection = _radioButtons.FirstOrDefault(x => x.Checked);
if (selection == null)
{
MessageBox.Show("Make a selection");
}
else
{
MessageBox.Show($"You selected {selection.Text}");
}
}
Edit: Working with both Panel and GroupBox
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace RadioButtonApp
{
public partial class Form1 : Form
{
private List<RadioButton> _radioButtonsGroupBox;
private List<RadioButton> _radioButtonsPanel;
public Form1()
{
InitializeComponent();
Shown += OnShown;
}
private void OnShown(object sender, EventArgs e)
{
_radioButtonsGroupBox = OptionsGroupBox.Controls.OfType<RadioButton>().ToList();
_radioButtonsGroupBox.ForEach(rb => rb.Checked = false);
_radioButtonsPanel = OptionsPanel.Controls.OfType<RadioButton>().ToList();
_radioButtonsPanel.ForEach(rb => rb.Checked = false);
}
private void CheckSelectionInGroupBoxButton_Click(object sender, EventArgs e)
{
var selection = _radioButtonsGroupBox.FirstOrDefault(x => x.Checked);
if (selection == null)
{
MessageBox.Show("Make a selection");
}
else
{
MessageBox.Show($"You selected {selection.Text}");
}
}
private void CheckSelectionInPanelButton_Click(object sender, EventArgs e)
{
var selection = _radioButtonsPanel.FirstOrDefault(x => x.Checked);
if (selection == null)
{
MessageBox.Show("Make a selection");
}
else
{
MessageBox.Show($"You selected {selection.Text}");
}
}
}
}
We have a DataGridView with data in a form. To enable quick search, we added TextBox to DataGridView.Controls and highlight cells which contain text from TextBox.
However, there is an issue. DataGridView consumes the Left arrow ←, Right arrow →, Home and End (with or without Shift) keys even if the cursor is in TextBox, and the user cannot change the caret position or select text from the keyboard.
TextBox generates a PreviewKeyDown event and nothing more happens.
Simplified code:
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
Width = 400;
Height = 400;
var txt = new TextBox { Dock = DockStyle.Bottom, BackColor = Color.Khaki };
var dgv = new DataGridView
{
Dock = DockStyle.Fill,
ColumnCount = 3,
RowCount = 5
};
dgv.Controls.Add(txt);
Controls.Add(dgv);
dgv.PreviewKeyDown += DgvOnPreviewKeyDown;
dgv.KeyDown += DgvOnKeyDown;
txt.PreviewKeyDown += TxtOnPreviewKeyDown;
txt.KeyDown += TxtOnKeyDown;
}
private void DgvOnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
Debug.WriteLine(String.Format("Dgv Key Preview {0}", e.KeyCode));
e.IsInputKey = true;
}
private void DgvOnKeyDown(object sender, KeyEventArgs e)
{
Debug.WriteLine(String.Format("Dgv Key {0}", e.KeyCode));
}
private void TxtOnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
Debug.WriteLine(String.Format("Txt Key Preview {0}", e.KeyCode));
}
private void TxtOnKeyDown(object sender, KeyEventArgs e)
{
Debug.WriteLine(String.Format("Txt Key {0}", e.KeyCode));
}
}
Type 123 in TextBox and then try the Right arrow, Left arrow, End, or Home. DataGridView change the selected cell, but the TextBox caret doesn't move.
TextBox works just fine if not inside a DataGridView (no problem at all when using the same method adding it into TreeView for example). TextBox acts similar to the Quick search Panel in the browser and has to be on top of the DataGridView. Adding a TextBox to a Form (or to be more specific, to a DataGridView parent) creates its own set of issues (tracking Location, Size, Visibility, ...) and is not acceptable.
What can be done to make sure that TextBox receive those keys and change the caret position or select text?
TextBox works just fine if not inside DataGridView (no problem at all when using the same method adding it into TreeView for example)
Apparently the problem is in DataGridView. It's because DataGridView overrides the Control.ProcessKeyPreview method:
This method is called by a child control when the child control receives a keyboard message. The child control calls this method before generating any keyboard events for the message. If this method returns true, the child control considers the message processed and does not generate any keyboard events.
The DataGridView implementation does just that - it maintains zero or one child controls internally (EditingControl), and when there is no such control active, it handles many keys (navigation, tab, enter, escape, etc.) by returning true, thus preventing the child TextBox keyboard events generation. The return value is controlled by the ProcessDataGridViewKey method.
Since the method is virtual, you can replace the DataGridView with a custom derived class which overrides the aforementioned method and prevents the undesired behavior when neither the view nor the view active editor (if any) has the keyboard focus.
Something like this:
public class CustomDataGridView : DataGridView
{
bool SuppressDataGridViewKeyProcessing => ContainsFocus && !Focused &&
(EditingControl == null || !EditingControl.ContainsFocus);
protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (SuppressDataGridViewKeyProcessing) return false;
return base.ProcessDataGridViewKey(e);
}
}
The above is just the half of the story and solves the cursor navigation and selection keys issue. However DataGridView intercepts another key message preprocessing infrastructure method - Control.ProcessDialogKey and handles Tab, Esc, Return, etc. keys there. So in order to prevent that, the method has to be overridden as well and redirected to the parent of the data grid view. The later needs a little reflection trickery to call a protected method, but using one time compiled delegate at least avoids the performance hit.
With that addition, the final custom class would be like this:
public class CustomDataGridView : DataGridView
{
bool SuppressDataGridViewKeyProcessing => ContainsFocus && !Focused &&
(EditingControl == null || !EditingControl.ContainsFocus);
protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (SuppressDataGridViewKeyProcessing) return false;
return base.ProcessDataGridViewKey(e);
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (SuppressDataGridViewKeyProcessing)
{
if (Parent != null) return DefaultProcessDialogKey(Parent, keyData);
return false;
}
return base.ProcessDialogKey(keyData);
}
static readonly Func<Control, Keys, bool> DefaultProcessDialogKey =
(Func<Control, Keys, bool>)Delegate.CreateDelegate(typeof(Func<Control, Keys, bool>),
typeof(Control).GetMethod(nameof(ProcessDialogKey), BindingFlags.NonPublic | BindingFlags.Instance));
}
You can try this.
I created my own textbox and overrode method ProcessKeyMessage.
public class MyTextBox : TextBox
{
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
protected override bool ProcessKeyMessage(ref Message m)
{
if (m.Msg != WM_SYSKEYDOWN && m.Msg != WM_KEYDOWN)
{
return base.ProcessKeyMessage(ref m);
}
Keys keyData = (Keys)((int)m.WParam);
switch (keyData)
{
case Keys.Left:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.ShiftKey:
return base.ProcessKeyEventArgs(ref m);
default:
return base.ProcessKeyMessage(ref m);
}
}
}
And then you can call:
var txt = new MyTextBox { Dock = DockStyle.Bottom, BackColor = Color.Khaki };
Try to just add the TextBox to the main form instead of the DataGridView:
Controls.Add(txt);
Controls.Add(dgv);
txt.PreviewKeyDown += DgvOnPreviewKeyDown;
txt.KeyDown += DgvOnKeyDown;
txt.PreviewKeyDown += TxtOnPreviewKeyDown;
txt.KeyDown += TxtOnKeyDown;
It sounds a bit like an exercise in futility.
It may be easier to encapsulate the behavior of both the TextBox and DataGridView controls by placing them into a UserControl together with a little code to handle events.
Here is a partial solution to the issue. TextBox still doesn't receive navigation keys input natively, but I reproduced a normal caret and selection behavior.
PreviewKeyDownEventArgs contains information about the pressed key and modifiers (Shift). For each key combination I set a new SelectionStart and SelectionLength for the TextBox.
private void TxtOnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
TextBox txt = (TextBox)sender;
if (e.KeyCode == Keys.Home)
{
int idx = txt.SelectionStart;
txt.SelectionStart = 0;
txt.SelectionLength = e.Shift ? idx : 0;
}
else if (e.KeyCode == Keys.End)
{
int idx = txt.SelectionStart;
if (e.Shift)
txt.SelectionLength = txt.TextLength - idx;
else
{
txt.SelectionStart = txt.TextLength;
txt.SelectionLength = 0;
}
}
else if (e.KeyCode == Keys.Left)
{
if (e.Shift)
{
if (txt.SelectionStart > 0)
{
txt.SelectionStart--;
txt.SelectionLength++;
}
}
else
{
txt.SelectionStart = Math.Max(0, txt.SelectionStart - 1);
txt.SelectionLength = 0;
}
}
else if (e.KeyCode == Keys.Right)
{
if (e.Shift)
txt.SelectionLength++;
else
{
txt.SelectionStart = Math.Min(txt.TextLength, txt.SelectionStart + 1);
txt.SelectionLength = 0;
}
}
}
I am trying to make my custom ComboBox inheriting from ContainerControl. I used this article as a base but rewrote it, but I use a ToolStripControlHost, my own custom ListBox & a ToolStripDropDown.
Now the ComboBox is a button where you click on to show the DropDowncontaining my ListBox, works fine with overriding OnMouseClick.
The problems starts when I try to close the DropDown, with the DropDown's 'AutoClose' property to true, the DropDown closes if you click somewhere outside the DropDown (including the button) ...
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
/* listboxControl = ToolStripDropDown */
if (!listboxControl.Visible)
{
listboxControl.Show(this, GetDropLocation(), ToolStripDropDownDirection.BelowRight);
//listbox.Capture = true;
}
}
This is the code for the click on the button .. so what happens if you click it ?
If the DropDown is shown, it first closes the DropDown, then it fires the OnMouseClick event. Meaning: listboxControl.Visible is already false & it will show the DropDown again. All of this causing a quick close-open.
I have been stuck with this problem for some time now and google doesn't seem to know a lot about this subject (that article on CodeProject has the same bug).
What I have tried is disabling AutoClose and capturing the mouse after I show the DropDown, this works partially but it affects the working of my hosted ListBox. The ListBox contains a set of controls (the items), these items have a hover paint effect. Capturing the mouse in the ListBox control prevents the OnMouseEnter to be fired.
All input would be greatly appreciated !
You need a variable to track the cursor position when the DropDown is closing.
Here is a quick and dirty example control:
public class CustomDropBox : Control {
private ListBox box = new ListBox() { IntegralHeight = false };
private ToolStripControlHost host;
private ToolStripDropDown drop;
private bool wasShowing = false;
public CustomDropBox() {
box.MinimumSize = new Size(120, 120);
box.MouseUp += box_MouseUp;
box.KeyPress += box_KeyPress;
box.Items.AddRange(new string[] { "aaa", "bbb", "ccc" });
host = new ToolStripControlHost(box) { Padding = Padding.Empty };
drop = new ToolStripDropDown() { Padding = Padding.Empty };
drop.Closing += drop_Closing;
drop.Items.Add(host);
}
private Rectangle GetDownRectangle() {
return new Rectangle(this.ClientSize.Width - 16, 0, 16, this.ClientSize.Height);
}
void drop_Closing(object sender, ToolStripDropDownClosingEventArgs e) {
if (e.CloseReason == ToolStripDropDownCloseReason.AppClicked) {
wasShowing = GetDownRectangle().Contains(this.PointToClient(Cursor.Position));
} else {
wasShowing = false;
}
}
void box_KeyPress(object sender, KeyPressEventArgs e) {
if (e.KeyChar == (char)Keys.Enter && box.SelectedIndex > -1) {
drop.Close();
}
}
void box_MouseUp(object sender, MouseEventArgs e) {
int index = box.IndexFromPoint(e.Location);
if (index > -1) {
drop.Close();
}
}
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left && GetDownRectangle().Contains(e.Location)) {
if (wasShowing) {
wasShowing = false;
} else {
drop.Show(this, new Point(0, this.Height));
}
}
base.OnMouseDown(e);
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(Color.White);
ControlPaint.DrawComboButton(e.Graphics, GetDownRectangle(), ButtonState.Normal);
base.OnPaint(e);
}
}
I have a lot of TextBoxes in my single winform application. I am looking for a way to bind a single event method to all those textboxes when the form loads or in its constructor, so I dont add the event to every single textbox in designer.
In the event, I want to detect the ENTER key and then programmatically click on a button:
private void ApplyFilterOnEnterKey(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
btnApplyFilters_Click(this, null);
}
}
Now the problem is how can I loop over all textboxes in my form and bind them to the above method? My textboxes are everywhere, inside nested tablelayoutpanels or nested normal pannels. How this loop will look like and where should I put it? In form constructor or in load event?!
Instead of subscribing to every TextBox's KeyDown event, you have two other options that I think are better:
Set your button as the default button of the form by setting AcceptButton property of the form to the button you want to be clicked by pressing Enter key.
Override ProcessDialogKey on your form and check for pressing the Enter key:
protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Enter)
{
// ... do what you want
return true;
}
else
return base.ProcessDialogKey(keyData);
}
Just use the Controls collection and look if the control is a textbox then append the event
Loop through all textboxes (including nested) like this as shown here: Loop through Textboxes
Then,
var allTextBoxes = this.GetChildControls<TextBox>();
foreach (TextBox tb in this.GetChildControls<TextBox>())
{
tb.Click += ApplyFilterOnEnterKey;
}
private void TextBoxFocusIn(object sender, EventArgs e)
{
TextBox textBox = (TextBox)sender;
if (textBox.Text == "Encrypted value here...")
{
textBox.Text = "";
textBox.ForeColor = Color.Black;
}
}
private void TextBoxFocusOut(object sender, EventArgs e)
{
TextBox textBox = (TextBox)sender;
if (textBox.Text =="")
{
textBox.Text = "Encrypted value here...";
textBox.ForeColor = Color.Gray;
}
}
private void BindPlaceHolderInTextbox(Panel contentPanel)
{
foreach(Control control in contentPanel.Controls)
{
if(control.GetType() == typeof(TextBox))
{
control.Text = "Encrypted value here...";
control.ForeColor = Color.Gray;
control.GotFocus += new System.EventHandler(TextBoxFocusIn);
control.LostFocus += new System.EventHandler(TextBoxFocusOut);
}
}
}
How do you edit items and subitems in a listview? Let's say I have a listview with 3 columns,and subitems,
Car Brand | Car Name | Car Year
Ford | Mustang | 2000
Dodge | Charger | 2007
How would I Add items like that to listview and how would I edit let's say the Car Name on which ever row by index[] if I needed to edit at runtime at firing of an event?
If you're looking for "in-place" editing of a ListView's contents (specifically the subitems of a ListView in details view mode), you'll need to implement this yourself, or use a third-party control.
By default, the best you can achieve with a "standard" ListView is to set it's LabelEdit property to true to allow the user to edit the text of the first column of the ListView (assuming you want to allow a free-format text edit).
Some examples (including full source-code) of customized ListView's that allow "in-place" editing of sub-items are:
C# Editable ListView
In-place editing of ListView subitems
I use a hidden textbox to edit all the listview items/subitems. The only problem is that the textbox needs to disappear as soon as any event takes place outside the textbox and the listview doesn't trigger the scroll event so if you scroll the listview the textbox will still be visible.
To bypass this problem I created the Scroll event with this overrided listview.
Here is my code, I constantly reuse it so it might be help for someone:
ListViewItem.ListViewSubItem SelectedLSI;
private void listView2_MouseUp(object sender, MouseEventArgs e)
{
ListViewHitTestInfo i = listView2.HitTest(e.X, e.Y);
SelectedLSI = i.SubItem;
if (SelectedLSI == null)
return;
int border = 0;
switch (listView2.BorderStyle)
{
case BorderStyle.FixedSingle:
border = 1;
break;
case BorderStyle.Fixed3D:
border = 2;
break;
}
int CellWidth = SelectedLSI.Bounds.Width;
int CellHeight = SelectedLSI.Bounds.Height;
int CellLeft = border + listView2.Left + i.SubItem.Bounds.Left;
int CellTop =listView2.Top + i.SubItem.Bounds.Top;
// First Column
if (i.SubItem == i.Item.SubItems[0])
CellWidth = listView2.Columns[0].Width;
TxtEdit.Location = new Point(CellLeft, CellTop);
TxtEdit.Size = new Size(CellWidth, CellHeight);
TxtEdit.Visible = true;
TxtEdit.BringToFront();
TxtEdit.Text = i.SubItem.Text;
TxtEdit.Select();
TxtEdit.SelectAll();
}
private void listView2_MouseDown(object sender, MouseEventArgs e)
{
HideTextEditor();
}
private void listView2_Scroll(object sender, EventArgs e)
{
HideTextEditor();
}
private void TxtEdit_Leave(object sender, EventArgs e)
{
HideTextEditor();
}
private void TxtEdit_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
HideTextEditor();
}
private void HideTextEditor()
{
TxtEdit.Visible = false;
if (SelectedLSI != null)
SelectedLSI.Text = TxtEdit.Text;
SelectedLSI = null;
TxtEdit.Text = "";
}
Click the items in the list view.
Add a button that will edit the selected items.
Add the code
try
{
LSTDEDUCTION.SelectedItems[0].SubItems[1].Text = txtcarName.Text;
LSTDEDUCTION.SelectedItems[0].SubItems[0].Text = txtcarBrand.Text;
LSTDEDUCTION.SelectedItems[0].SubItems[2].Text = txtCarName.Text;
}
catch{}
Sorry, don't have enough rep, or would have commented on CraigTP's answer.
I found the solution from the 1st link - C# Editable ListView, quite easy to use. The general idea is to:
identify the SubItem that was selected and overlay a TextBox with the SubItem's text over the SubItem
give this TextBox focus
change SubItem's text to that of TextBox's when TextBox loses focus
What a workaround for a seemingly simple operation :-|
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
li = listView1.GetItemAt(e.X, e.Y);
X = e.X;
Y = e.Y;
}
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
int nStart = X;
int spos = 0;
int epos = listView1.Columns[1].Width;
for (int i = 0; i < listView1.Columns.Count; i++)
{
if (nStart > spos && nStart < epos)
{
subItemSelected = i;
break;
}
spos = epos;
epos += listView1.Columns[i].Width;
}
li.SubItems[subItemSelected].Text = "9";
}