I wrote the following code to make the comboBox1
ReadOnly
.
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
And, I also have the code like this:
private void comboBox1_DropDown(object sender, EventArgs e)
{
comboBox1.Items.Clear();
... // Logic to automatically add items to comboBox1
}
The problem is dissappear that a previously selected value whenever an event occurs. Someone points out that it is because of the use of clear()
. But, it doesn't occur this problem when DropDownStyle
is DropDown
.
How do I should solve this problem?
The key is that it can't input in comboBox other than user selecting value.
CodePudding user response:
Here is an alternative to using Clear
.
One solution is to have a binding list that is the data source of your ComboBox. When you enumerate the available COM ports (shown below as a simulation) you will add or remove com ports from the binding source based on availability. This should result in the behavior you want, because if a com port is "still" available the selection won't change.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
comboBoxCom.DropDownStyle= ComboBoxStyle.DropDownList;
comboBoxCom.DataSource = MockDataSource;
refreshAvailableComPorts(init: true);
comboBoxCom.DropDown = onComboBoxComDropDown;
}
BindingList<string> MockDataSource = new BindingList<string>();
private void onComboBoxComDropDown(object? sender, EventArgs e)
{
refreshAvailableComPorts(init: false);
}
/// <summary>
/// This is a simulation that will choose a different
/// set of "available" ports on each drop down.
/// </summary>
private void refreshAvailableComPorts(bool init)
{
if(init)
{
MockDataSource.Clear();
MockDataSource.Add("COM2");
}
else
{
string
selB4 = string.Join(",", MockDataSource),
selFtr;
do
{
var flags = _rando.Next(1, 0x10);
if ((flags & 0x1) == 0) MockDataSource.Remove("COM1");
else if(!MockDataSource.Contains("COM1")) MockDataSource.Add("COM1");
if ((flags & 0x2) == 0) MockDataSource.Remove("COM2");
else if (!MockDataSource.Contains("COM2")) MockDataSource.Add("COM2");
if ((flags & 0x4) == 0) MockDataSource.Remove("COM3");
else if (!MockDataSource.Contains("COM3")) MockDataSource.Add("COM3");
if ((flags & 0x8) == 0) MockDataSource.Remove("COM4");
else if (!MockDataSource.Contains("COM4")) MockDataSource.Add("COM4");
selFtr = string.Join(",", MockDataSource);
if (!selFtr.Equals(selB4))
{
break;
}
} while (true);
}
}
private Random _rando = new Random(5);
}
CodePudding user response:
Based on your helpful comment, it appears that your "underlying" reason for posting is wanting to keep the ComboBox
up to date with COM ports that are currently connected.
I don't make a habit of posting multiple answers on the same thread, but there "might" be a more optimal solution to the thing you were trying to do in the first place (this is known as an
BindingList<ComPort> ComPorts = new BindingList<ComPort>();
protected override void onl oad(EventArgs e)
{
base.OnLoad(e);
comboBoxCom.DropDownStyle = ComboBoxStyle.DropDownList;
comboBoxCom.DropDownClosed = (sender, e) => ActiveControl = null;
comboBoxCom.DataSource = ComPorts;
ComPorts.ListChanged = onComPortChanged;
enumerateComPorts();
}
bool _isDeviceChange = false;
private void onComPortChanged(object? sender, ListChangedEventArgs e)
{
if(_isDeviceChange) BeginInvoke(() => richTextBox.Clear());
ComPort comPort;
switch (e.ListChangedType)
{
case ListChangedType.ItemAdded:
comPort = ComPorts[e.NewIndex];
using (ManagementClass manager = new ManagementClass("Win32_PnPEntity"))
{
foreach (ManagementObject record in manager.GetInstances())
{
var pnpDeviceId = record.GetPropertyValue("PnpDeviceID")?.ToString();
if (pnpDeviceId != null)
{
var subkey = Path.Combine("System", "CurrentControlSet", "Enum", pnpDeviceId, "Device Parameters");
var regKey = Registry.LocalMachine.OpenSubKey(subkey);
if (regKey != null)
{
var names = regKey.GetValueNames();
if (names.Contains("PortName"))
{
var portName = regKey.GetValue("PortName");
if (Equals(comPort.PortName, portName))
{
var subkeyParent = Path.Combine("System", "CurrentControlSet", "Enum", pnpDeviceId);
var regKeyParent = Registry.LocalMachine.OpenSubKey(subkeyParent);
comPort.FriendlyName = $"{regKeyParent?.GetValue("FriendlyName")}";
comPort.PnpDeviceId = pnpDeviceId;
}
}
}
}
}
}
break;
case ListChangedType.ItemDeleted:
comPort = _removedItem!;
break;
default: return;
}
BeginInvoke(() =>
{
if (_isDeviceChange)
{
richTextBox.SelectionColor =
e.ListChangedType.Equals(ListChangedType.ItemAdded) ?
Color.Green : Color.Red;
richTextBox.AppendText($"{e.ListChangedType}{Environment.NewLine}");
}
else
{
richTextBox.SelectionColor = Color.Blue;
richTextBox.AppendText($"Detected{Environment.NewLine}");
}
richTextBox.SelectionColor = Color.FromArgb(0x20, 0x20, 0x20);
richTextBox.AppendText(comPort?.GetFullDescription());
});
}
int _wdtCount = 0;
private ComPort? _removedItem = null;
private void onDeviceChange()
{
_isDeviceChange = true;
int captureCount = _wdtCount;
Task
.Delay(TimeSpan.FromMilliseconds(500))
.GetAwaiter()
.OnCompleted(() =>
{
if(captureCount.Equals(_wdtCount))
{
// The events have settled out
enumerateComPorts();
}
});
}
private void enumerateComPorts()
{
string[] ports = SerialPort.GetPortNames();
foreach (var port in ports)
{
if (!ComPorts.Any(_ => Equals(_.PortName, port)))
{
ComPorts.Add(new ComPort { PortName = port });
}
}
foreach (var port in ComPorts.ToArray())
{
if (!ports.Contains(port.PortName))
{
_removedItem = port;
ComPorts.Remove(port);
}
}
BeginInvoke(()=> ActiveControl = null); // Remove focus rectangle
}
The DropDown
event of the combo box is now no longer required. Problem solved!