How to make Combobox in winforms readonly - c#

I do not want the user to be able to change the value displayed in the combobox. I have been using Enabled = false but it grays out the text, so it is not very readable. I want it to behave like a textbox with ReadOnly = true, where the text is displayed normally, but the user can't edit it.
Is there is a way of accomplishing this?

make DropDownStyle property to DropDownList instead of DropDown
then handle the TextChanged event to prevent user changing text.

The article ComboBox-with-read-only-behavior suggests an interesting solution:
Create both a readonly textbox and a combobox in the same place. When you want readonly mode, display the textbox, when you want it to be editable, display the combobox.

Not sure if this is what you're looking for but...
Set the DropDownStyle = DropDownList
Then on the SelectedIndexChanged event
if (ComboBox1.SelectedIndex != 0)
{
ComboBox1.SelectedIndex = 0;
}
This ugly part is that they will "feel" like they can change it. They might think this is an error unless you give them an alert telling them why they can't change the value.

The best thing I can suggest is to replace the combo-box with a read-only textbox (or just perhaps a label) - that way the user can still select/copy the value, etc.
Of course, another cheeky tactic would be to set the DropDownStyle to DropDownList, and just remove all other options - then the user has nothing else to pick ;-p

enter link description here
Just change the DropDownStyle to DropDownList. Or if you want it completely read only you can set Enabled = false, or if you don't like the look of that I sometimes have two controls, one readonly textbox and one combobox and then hide the combo and show the textbox if it should be completely readonly and vice versa.

Here is the Best solution for the ReadOnly Combo.
private void combo1_KeyPress(object sender, KeyPressEventArgs e)
{
e.KeyChar = (char)Keys.None;
}
It will discard the keypress for the Combo.

I've handled it by subclassing the ComboBox to add a ReadOnly property that hides itself when set and displays a ReadOnly TextBox on top containing the same Text:
class ComboBoxReadOnly : ComboBox
{
public ComboBoxReadOnly()
{
textBox = new TextBox();
textBox.ReadOnly = true;
textBox.Visible = false;
}
private TextBox textBox;
private bool readOnly = false;
public bool ReadOnly
{
get { return readOnly; }
set
{
readOnly = value;
if (readOnly)
{
this.Visible = false;
textBox.Text = this.Text;
textBox.Location = this.Location;
textBox.Size = this.Size;
textBox.Visible = true;
if (textBox.Parent == null)
this.Parent.Controls.Add(textBox);
}
else
{
this.Visible = true;
this.textBox.Visible = false;
}
}
}
}

Michael R's code works, but...
The DropDownHeight = 1; must be back to the default value when ReadOnly property is set to false. So, insert before base.OnDropDown(e): DropDownHeight = 106;
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test_Application
{
class ReadOnlyComboBox : ComboBox
{
private bool _readOnly;
private bool isLoading;
private bool indexChangedFlag;
private int lastIndex = -1;
private string lastText = "";
public ReadOnlyComboBox()
{
}
public bool ReadOnly
{
get { return _readOnly; }
set { _readOnly = value; }
}
protected override void OnDropDown (EventArgs e)
{
if (_readOnly)
{
DropDownHeight = 1;
var t = new Thread(CloseDropDown);
t.Start();
return;
}
DropDownHeight = 106; //Insert this line.
base.OnDropDown(e);
}
private delegate void CloseDropDownDelegate();
private void WaitForDropDown()
{
if (InvokeRequired)
{
var d = new CloseDropDownDelegate (WaitForDropDown);
Invoke(d);
}
else
{
DroppedDown = false;
}
}
private void CloseDropDown()
{
WaitForDropDown();
}
protected override void OnMouseWheel (MouseEventArgs e)
{
if (!_readOnly)
base.OnMouseWheel(e);
}
protected override void OnKeyDown (KeyEventArgs e)
{
if (_readOnly)
{
switch (e.KeyCode)
{
case Keys.Back:
case Keys.Delete:
case Keys.Up:
case Keys.Down:
e.SuppressKeyPress = true;
return;
}
}
base.OnKeyDown(e);
}
protected override void OnKeyPress (KeyPressEventArgs e)
{
if (_readOnly)
{
e.Handled = true;
return;
}
base.OnKeyPress(e);
}
}
}
To complete this answer:
File -> New -> Project... Visual C# -> Windows -> Classic Desktop ->
Windows Forms Control Library
type the Name of your control - OK and paste this code.
You can choose the name of your dll file:
Project - yourproject Properties...
Assembly name: type the name. Just build the solution and you have your dll file. So, open the project where you want to use your Read Only combo, right click on References
Add Reference... and browse your dll file. To Insert your custom component into Toolbox, open your Toolbox, right click on General tab -> Choose Items...
Browse your dll file - Open. Now you can use your ReadOnlyComboBox in your projects. PS: I'm using VS2015.

You can change the forecolor and backcolor to the system colors for an enabled combo box, although this may confuse the users (why have it if they can't change it), it will look better.

This is how you would address the fact that a ComboBox with Enabled = False is hard to read:
A combobox that looks decent when it is disabled

Actually, its rather simple:
Private Sub combobox1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles combobox1.KeyDown
' the following makes this the combobox read only
e.SuppressKeyPress = True
End Sub

Why don't you just use a text box? Text box has a "Read only" property, and since you want your combo box only to display data, I don't see why you would need a combo box.
An alternative is that you just cancel out the input for the "on value changed" event. That way you will be displaying your information no mater what user does ...

Set DropdownStyle Property to Simple
Add below code to to KeyPress event of ComboBox
private void comboBoxName_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = true;
return;
}
Add below code to to KeyDown event of ComboBox
private void comboBoxName_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
return;
}

If you've already populated it, and selected the appropriate item, and made it a DropDownList, then you can use an extension method like this to quickly reduce the selection list down to just the selected item:
public static void MakeReadOnly(this ComboBox pComboBox) {
if (pComboBox.SelectedItem == null)
return;
pComboBox.DataSource = new List<object> {
pComboBox.SelectedItem
};
}

I know that I'm a little late to the party, but I was researching this exact question and I knew that there had to be some way to make the combobox readonly as if it were a textbox and disabled the list popping up. It's not perfect, but it is definitely better than all of the answers I've been finding all over the internet that don't work for me. After the button is pressed and the OnDropDown is called, a new thread is created that will set the DroppedDown property to false, thus creating the effect of "nothing happening." The mouse wheel is consumed and key events are consumed as well.
using System;
using System.Threading;
using System.Windows.Forms;
namespace Test_Application
{
class ReadOnlyComboBox : ComboBox
{
private bool _readOnly;
private bool isLoading;
private bool indexChangedFlag;
private int lastIndex = -1;
private string lastText = "";
public ReadOnlyComboBox()
{
}
public bool ReadOnly
{
get { return _readOnly; }
set { _readOnly = value; }
}
protected override void OnDropDown(EventArgs e)
{
if (_readOnly)
{
DropDownHeight = 1;
var t = new Thread(CloseDropDown);
t.Start();
return;
}
base.OnDropDown(e);
}
private delegate void CloseDropDownDelegate();
private void WaitForDropDown()
{
if (InvokeRequired)
{
var d = new CloseDropDownDelegate(WaitForDropDown);
Invoke(d);
}
else
{
DroppedDown = false;
}
}
private void CloseDropDown()
{
WaitForDropDown();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
if (!_readOnly)
base.OnMouseWheel(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (_readOnly)
{
switch (e.KeyCode)
{
case Keys.Back:
case Keys.Delete:
case Keys.Up:
case Keys.Down:
e.SuppressKeyPress = true;
return;
}
}
base.OnKeyDown(e);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (_readOnly)
{
e.Handled = true;
return;
}
base.OnKeyPress(e);
}
}
}

Simplest way in code:
instead of adding methods for KeyPress or KeyDown,
add this code on 'Form1_Load' method:
comboBox1.KeyPress += (sndr, eva) => eva.Handled = true;
or
comboBox1.KeyDown += (sndr, eva) => eva.SuppressKeyPress = true;
(sndr, eva) is for (object sender, EventArgs e)

I dont know if that is what you are looking but this prevents the user from chosing any item from the drop down and still be able to type text in the combobox. If you dont want the user to type text in the combobox you can make it Dropdown list from the properties menu.
So you get Read Only combobox.
On Selected Index Changed
Make the selected Index -1 "comboBox.SelectedIndex = -1";
private void MyComboBox_comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
MyComboBox_comboBox.SelectedIndex = -1;
}

Here is the Best solution for the ReadOnly Combo.
private void combo1_KeyPress(object sender, KeyPressEventArgs e) {
e.KeyChar = (char)Keys.None;
}
It will discard the keypress for the Combo. It doesn't have "e.KeyChar" !

Related

Is it possible to tell if the Text change on an entry was from code or from the UI?

I am working on a Xamarin project and I need to be able to tell if the changes that occur to the text in an Entry view are from the code or from the UI, is this possible in Xamarin? or is there a known work around to do this.
I know about the OnTextChanged event but this only tells you that the Text property has changed, and gives you access to the old and new value of the Text property. It does not differentiate between different causes of text change.
You can get some idea from this thread, check if the entry is focused to differentiate between different causes of text change:
public MainPage()
{
InitializeComponent();
myEntry.TextChanged += MyEntry_TextChanged;
}
private void MyEntry_TextChanged(object sender, TextChangedEventArgs e)
{
var entry = sender as Entry;
if (entry.IsFocused)
{
//change from UI
Console.WriteLine("change from UI");
}
else{
//change from code
Console.WriteLine("change from code");
}
}
Update: The better way to solve op's problem:
You can set a flag yourself that tells your code to ignore the event. For example:
private bool ignoreTextChanged;
private void textNazwa_TextCanged(object sender, EventArgs e)
{
if (ignoreTextChanged) return;
}
Create a method and use this to set the text instead of just calling Text = "...";::
private void SetTextBoxText(TextBox box, string text)
{
ignoreTextChanged = true;
box.Text = text;
ignoreTextChanged = false;
}
Refer: ignoreTextChanged
you can use EntryRenderer to detect keypress event and use that flag to detect the change by code or by UI.
Here are the step:
- Exetend your entry control with new event OnTextChangeByUI
- Write custom render for both platform
e.g for android it will be something like this
public class ExtendedEntryRender : EntryRenderer
{
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control != null)
{
Control.KeyPress += ((Entry)Element).OnTextChangeByUI;
}
}
}

checking the pressed key when closing datetimepicker

When datetimepicker is used, sometimes user does not do any changes and close the datetimepicker.
After closing the datetimepicker my program checks if the date is suitable with the rest of data. I want to prevent this checking if datetimepicker is closed by escape or clicking somewhere else.
Is it possible to detect if datetimepicker is closed without choosen a date?
I use close-up property and EventArgs.
I think the problem here is that the ValueChanged event fires even when the User is just browsing through the calendar. I don't see a really obvious way handle it. However the following code will do a decent job of ignoring value changes events between the DropDown and CloseUp events.
bool UserIsJustLooking = false;
private void dateTimePicker1_DropDown(object sender, EventArgs e)
{
UserIsJustLooking = true;
dateTimePicker1.Tag = dateTimePicker1.Value;
}
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
if (UserIsJustLooking)
{
// the user is just browsing the dates (ignore these value changed events because they aren't real)
}
else
{
Console.WriteLine("The value changed without opening, new value is " + dateTimePicker1.Value.ToString());
}
}
private void dateTimePicker1_CloseUp(object sender, EventArgs e)
{
UserIsJustLooking = false;
if ((DateTime)dateTimePicker1.Tag == dateTimePicker1.Value)
{
// User did not really change the value
}
else
{
Console.WriteLine("User selected a new value: " + dateTimePicker1.Value);
}
}
You can use the built-in validation methods:
private void DateTimePicker_Validating(object sender, CancelEventArgs e)
{
//validation logic
//if valid
e.Cancel = false;
//else
e.Cancel = true;
}
Setting the CancelEventArgs.Cancel to true will prevent the control from loosing focus.
You need to attach this to your DateTimePicker Validating event
After the illumination of David as I could not resolve the problem with
if ((DateTime)dateTimePicker1.Tag == dateTimePicker1.Value)
I created another version.
In public partial class I added these
public bool EscPressed = false;
public string calTag = DateTime.Now.ToString("d");
I wrote this method
private void cal_Close(object sender, EventArgs e)
{
EscPressed = false;
if (calTag == cal.Value.ToString("d"))
EscPressed = true;
else
EscPressed = false;
cal.Visible = false;
if (EscPressed)
return;
calTag = cal.Value.ToString("d");
_textBox[1].Text = _textBox1[1].Text = calTag;
CheckDate();
}
PS: Checkdate() is a method which checks if date is well chosen or not

Switching to a tab in TabControl using code

I have a tabcontrol in my application that has several tabs in it.
I want to automatically switch to another tab when the "Next" button is pressed.
I cannot figure out how to change which tab is visible programmatically.
private void Next_Click(object sender, EventArgs e)
{
// Change to the next tab
tabControl1.???;
}
Use the TabControl.SelectedTab property. MSDN.
tabControl1.SelectedTab = anotherTab;
But you can also use the TabControl.SelectedIndex property. MSDN.
try
{
tabControl1.SelectedIndex += 1;
}
catch
{
//This prevents the ArgumentOutOfRangeException.
}
For this particular scenario you can use SelectedIndex property of the TabControl. This gives you an integer representing the index of the currently selected tab. Likewise you can set a tab as selected by setting an integer value to this property.
private void btnNext_Click(object sender, EventArgs e)
{
int currentTabIndex = tabControl1.SelectedIndex;
currentTabIndex++;
if (currentTabIndex < tabControl1.TabCount)
{
tabControl1.SelectedIndex = currentTabIndex;
}
else
{
btnNext.Enabled=false;
}
}

C# editable listview

Yesterday I try to implement a new listview that support sub-item edit, my solution is to show a textbox when double click the sub-item. The key code as following:
protected override void OnDoubleClick(EventArgs e)
{
Point pt = this.PointToClient(Cursor.Position);
ListViewItem curItem;
int subItemIndex = GetSubItemAt(pt.X, pt.Y, out curItem);
DoubleClickEventArgs args = new DoubleClickEventArgs(subItemIndex);
base.OnDoubleClick(args);
if (subItemIndex>=0 && !args.Cancel)
{
//StartEdit(...);
}
}
public void EndEdit(bool acceptChanges)
{
//validation
.................
.................
AfterSubItemEventArgs e = new AfterSubItemEventArgs(this.SelectedItems[0], m_editSubItemIndex, this.SelectedItems[0].SubItems[m_editSubItemIndex].Text, m_textbox.Text, false);
OnAfterSubItemEdit(e);
if (e.Cancel)
{
//....
}
else
{
//set new value
}
m_textbox.Visible = false;
m_editSubItemIndex = -1;
}
OnAfterSubItemEdit is a event that user can do some validations or other operations. I add a check in this method, if the new value exist, I will show a messagebox to user firstly, then hide the textbox. But now, the problem comes, when i move the mouse, the listview items can be selected, I don't how to solve this issue, I tried my best to find out the way, but failed. So, please help me!
Listview has a LabelEdit property; when you set it "true", then in an event handler you can call Listview.Items[x].BeginEdit(), and edit an item. As an example, you can handle ListView.DoubleClick event and call BeginEdit right there:
private void Form1_Load(object sender, System.EventArgs e)
{
listView1.LabelEdit = true;
}
private void listView1_DoubleClick(object sender, System.EventArgs e)
{
if(this.listView1.SelectedItems.Count==1)
{
this.listView1.SelectedItems[0].BeginEdit();
}
}
The problem is that your form still calls the DoubleClick event whether the value exists or not. Add appropriate condition before calling base DoubleClick in your code, i.e.:
if(!new value exists)
base.OnDoubleClick(args);

How to determine whether TextChanged was triggered by keyboard in C#?

I have a method
private void textBoxPilot_TextChanged(object sender, TextChangedEventArgs e)
{ ... }
where the textbox in question takes a search string from the user and populates a ListBox with the results on every keystroke.
Subsequently, when an item is picked from the ListBox, I would like the choice reflected in the same Textbox. However, I don't want to trigger the search mechanism, which would cause the Listbox to forget its selection.
How can I determine whether the TextChanged event was triggered by the user (via they keyboard or maybe copy/paste) or by another method using textBoxPilot.Text = "Pilot name";?
Thanks.
bit of a hack, but....
public class MyForm : Form
{
private bool _ignoreTextChanged;
private void listView1_SelectionChanged( object sender, EventArgs e )
{
_ingnoreTextChanged = true;
textBoxPilot.Text = listView1.SelectedValue.ToString(); // or whatever
}
private void textBoxPilot_TextChanged( object sender, TextChangedEventArgs e )
{
if( _ignoreTextChanged )
{
_ignoreTextChanged = false;
return;
}
// Do what you would normally do.
}
}
A disabled control will not fire a event. So two options are either always disable update the text then re-enable or create a derived class wrapper (using this method you could still do data binding)
class myClass : TextBox
{
public virtual string TextWithoutEvents
{
get
{
return base.Text;
}
set
{
bool oldState = Enabled;
Enabled = false;
base.Text = value;
Enabled = oldState;
}
}
}
If the user selects "Pilot name" from the list, you set the text box to "Pilot name". This will cause the list box to select "Pilot name". So the selection should be kept. You just have to break the recursion.
In my scenario where user has to type in text to trigger auto-complete and we didn't want a re-trigger when the auto-complete changes the text again, I used the text lengths. This won't work if user copy/pastes and therefore adds more than 1 character at a time with the keyboard.
private void HandleTextChanged(object sender, TextChangedEventArgs e){
var oldText = e.OldTextValue;
var newText = e.NewTextValue;
// Assuming text changed from keyboard is always 1 character longer,
// ignore this text changed event if new text > 1 character longer.
if (newText.Length > oldText.Length + 1) {
return;
}
...
}
In your scenario, if you always know the values you want to skip, then you could check for them instead:
if (newText == "Pilot name") {
return;
}
or
if (myListOfNamesToIgnore.Contains(newText)) {
return;
}

Categories