Multiple Listboxes, One Selection - vb.net

I have 2 List Boxes on my form, but I only want to be able to make a single selection between both, how would I go about this?
I, of course, tried doing this first...
Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged
ListBox2.ClearSelected()
End Sub
Private Sub ListBox2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox2.SelectedIndexChanged
ListBox1.ClearSelected()
End Sub
But since that causes the selected index to be changed it clears what you have just selected as well...
Thanks for any help!

You can test the reason why Selected index changed, something like that:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e) {
// If listBox2 focused i.e. user clicked list box2 - do nothing:
// one should not try to clear list box2 selection
if (listBox2.Focused)
return;
listBox2.ClearSelected();
}
private void listBox2_SelectedIndexChanged(object sender, EventArgs e) {
// If list box1 is in focus that means that user just have selected itme there;
// so, do nothing: we shouldn't erase user's selection
if (listBox1.Focused)
return;
listBox1.ClearSelected();
}

First, your question is tagged as C# but the code sample is in VB.
Second, I would do it in C# as follows:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
listBox2.SelectedIndexChanged -= new System.EventHandler(this.listBox2_SelectedIndexChanged);
listBox2.ClearSelected();
listBox2.SelectedIndexChanged += new System.EventHandler(this.listBox2_SelectedIndexChanged);
}
And do the same with the other event handler.

Related

How do I prevent mouse wheel from incrementing numericupdown without over loading?

How do I prevent mouse wheel from incrementing numericupdown without over loading?
I had previously inherited numericupdwon to overload the MouseWheel event with an empty event. This worked for a while, but something happened when I switched to x64 that made the whole inherited class periodically show not found. Not sure because even if I switched back to x86 it was still a problem.
This worked for me..
Private Sub NumericUpDown1_MouseWheel(sender As Object, e As MouseEventArgs) Handles NumericUpDown1.MouseWheel
Dim MW As HandledMouseEventArgs = CType(e, HandledMouseEventArgs)
MW.Handled = True
End Sub
That HandledMouseEventArgs usage does look weird though.. but it works.
https://msdn.microsoft.com/en-us/library/system.windows.forms.handledmouseeventargs(v=vs.110).aspx
I came up with a different solution, using the following code to prevent the increment
Private Sub nup_cores_MouseWheel(sender As Object, e As MouseEventArgs) Handles nup_cores.MouseWheel
nup_cores.Increment = 0
End Sub
And then change it back. In the specific case of a numericupdown, the cursor blinking seems to trigger gotfocus. The same principle could be applied with a short duration timer
Private Sub nup_cores_GotFocus(sender As Object, e As EventArgs) Handles nup_cores.GotFocus
nup_cores.Increment = 1
End Sub
For C#:
For some reason the MouseWheel doesn't show up in the list of events from the GUI. So I had to programmatically add the event in my form load. In the MouseWheel event I do something similar as the above selected answer (but a little different).
private void Form1_Load(object sender, EventArgs e)
{
numericUpDownX.MouseWheel += new MouseEventHandler(handle_MouseWheel);
}
void handle_MouseWheel(object sender, MouseEventArgs e)
{
((HandledMouseEventArgs)e).Handled = true;
}

I want to show a hidden object on form 1 from form 2

So I have this PictureBox and Label on form1 which is hidden and I want to show it when you press a Button in form2.
Form1 code:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Label3.Hide()
Me.PictureBox3.Hide()
Form2 Code:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
PictureBox3.Show()
But picturebox3 is not declared in form2 so how can I access it?
There are several ways. As others have stated in answers, the following would be one solution
Declare a public property that returns the PictureBox in Form1
Pass an instance of Form1 to Form2, so Form2 can access this instance of Form1 and use the property
Source could look like this, for example:
In Form1
public PictureBox ThePictureBox
{
get { return this.pictureBox1; }
}
In Form2
private Form1 form1Instance;
public Form2(Form1 form1)
{
InitializeComponent();
form1Instance = form1;
}
public void Button_Click(object sender, EventArgs e)
{
this.form1Instance.ThePictureBox.Visible = true;
}
Another way would be: If Form2 is opened by Form1, you could declare an event in Form2, have Form1 subscribe to it and thus notify Form1 that the picture box should be visible. That way you do not have to expose a member that should otherwise be private to Form1.
This could look like this:
In Form1
private void OpenForm2()
{
Form2 f2 = new Form2();
f2.ShowPictureBox += ShowPictureBox;
f2.Show();
}
private void ShowPictureBox(object sender, EventArgs e)
{
this.pictureBox.Visible = true;
}
In Form2
public event EventHandler<EventArgs> ShowPictureBox;
protected void OnShowPictureBox()
{
if (ShowPictureBox != null)
ShowPictureBox(this, EventArgs.Empty);
}
private void Button_Click(object sender, EventArgs e)
{
OnShowPictureBox();
}
I'm aware of the fact that your example code is Visual Basic, but as you tagged the question "C#" I guess you should be able to translate this to VB.NET easily - my VB.NET is not fluent enough :-)
First of all, you need to be able to access your elements from the other forms (as you should make access modifiers internal or public).
Then you can call the related elements from another form by creating instance or passing the form to a method in other form. You should check this link for detailed explanation by the way.
In some situations, we need to use a control in more than one forms. This can be achieved by sharing the control between different forms
In your case, add the control in Form2 during button click, it should work.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Me.Controls.Add(Form1.PictureBox)
End Sub
Note: Form1.PictureBox is a shared property. We should define it like this in Form1.vb
public Shared Property PictureBox As PictureBox
Get
Return Me.PictureBox3
End Get
End Property

Radiobuttons in different group boxes

Quite a silly question, but still annoying.
The thing is that i have two group boxes where the titles have radiobuttons covering the group box titles.
Something like
(x) I want pizza
*Pizza stuff*
( ) I want Hamburger
*Hamburger stuff*
Since they're now in different group boxes, they can both be selected.
Is there a way to set/force the radiobuttons to be in the same "group"? Like in HTML where you set
name="WhatToEat" value="Pizza" for the first value and then
name="WhatToEat" value="Hamburger"
Or can i set the title for the groupbox to behave like a radio button or something?
Of course i can have the radio button outside the grop boxes, but i think having the titles as radiobuttons just makes the most sense and looks nicer.
If you have all your RadioButtons on a form. You can use a RadioButton variable to mark what is currently checked. Every time user checks a RadioButton, if it's not the currently checked RadioButton, make the currently checked RadioButton unchecked, and set the currently checked RadioButton to that RadioButton.
Here is my code:
public Form1(){
InitializeComponents();
currentChecked = radioButton1;
}
//Suppose the initially checked radio is radioButton1
RadioButton currentChecked;
//This is the CheckedChanged event handler used for all the radiobuttons
private void radioButtonChecked(object sender, EventArgs e)
{
RadioButton r = (RadioButton)sender;
if (r != currentChecked)
{
currentChecked.Checked = false;
currentChecked = r;
}
}
My code is much simpler without using any loop. It costs the additional currentChecked but it's not much.
Hope it helps!
No, it have to in one group ..
But for that case you can control in checked_change event
Private Sub RadioButton2_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadioButton2.CheckedChanged
RadioButton1.Checked = Not RadioButton2.Checked
End Sub
Private Sub RadioButton1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadioButton1.CheckedChanged
RadioButton2.Checked = Not RadioButton1.Checked
End Sub
Just move them to the Form at run-time. Use PointToScreen() and PointToClient() to keep them in the same position as you placed them at desing-time. So you'd replace "RadioButton1", "RadioButton2", and "RadioButton3" with your title RadioButtons:
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim RadioTitles() As RadioButton = {RadioButton1, RadioButton2, RadioButton3}
For Each rb As RadioButton In RadioTitles
Dim pt As Point = Me.PointToClient(rb.PointToScreen(New Point(0, 0)))
Me.Controls.Add(rb)
rb.Location = pt
rb.BringToFront()
Next
End Sub
*You could put a value into the Tag() property of each RadioButton that should be the "title" and then search for those instead of hard-coding them into the array. Or maybe you could name them a certain way.
EDIT: You could make the "title" RadioButtons enable/disable their associated GroupBoxes when they are checked like this:
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim RadioTitles() As RadioButton = {RadioButton1, RadioButton2, RadioButton3}
For Each rb As RadioButton In RadioTitles
rb.Parent.Enabled = False
rb.Tag = rb.Parent
AddHandler rb.CheckedChanged, AddressOf TitleRadioButtons_CheckedChanged
Dim pt As Point = Me.PointToClient(rb.PointToScreen(New Point(0, 0)))
Me.Controls.Add(rb)
rb.Location = pt
rb.BringToFront()
Next
End Sub
Private Sub TitleRadioButtons_CheckedChanged(sender As Object, e As System.EventArgs)
Dim rb As RadioButton = DirectCast(sender, RadioButton)
If Not IsNothing(rb.Tag) AndAlso TypeOf rb.Tag Is Control Then
Dim ctl As Control = DirectCast(rb.Tag, Control)
ctl.Enabled = rb.Checked
End If
End Sub
Unfortunately, that's not how radio buttons work.
As I'm sure you're aware, radio buttons get their grouping from containers. To my knowledge, if you want to be able to accomplish what you're asking, you'll probably need to code a custom solution. For instance, you could place an event handler on each radio button to fire off the same event to uncheck other boxes, e.g.
radioButton1.CheckedChanged += anyRadioButton_CheckedChanged;
radioButton2.CheckedChanged += anyRadioButton_CheckedChanged;
radioButton3.CheckedChanged += anyRadioButton_CheckedChanged;
...
private void anyRadioButton_CheckedChanged(object sender, EventArgs e)
{
foreach (var control in this.Controls)
{
if(control is GroupBox)
{
foreach (var childControl in ((GroupBox)control).Controls)
{
if (childControl is RadioButton && childControl != sender)
{
((RadioButton)childControl).Checked = false;
}
}
}
}
}
You can set text of the GroupBox to empty string and put RadioButton over it. The trick is to put it on form actually but move it the way it looks like part of GroupBox. But this is sufficient only if your group boxes are static and won't be moved. Otherwise it is better to use solution which #matzone proposed. But even in that case you can put all radio buttons in collection in your code and use the only event handler for all of them. Something like
private List<RadioButton> radioButtons;
public YourFormConstructor()
{
InitializeComponent();
radioButtons.Add(radio1);
radioButtons.Add(radio2);
radioButtons.Add(radio3);
foreach (var radio in radioButtons)
radio.CheckedChanged += RadioCheckedChanged;
}
private void CheckedChanged(object sender, EventArgs e)
{
var thisRadio = sender as RadioButton;
if (!thisRadio.Checked)
return;
foreach (var radio in radioButtons)
if (radio != thisRadio)
radio.Checked = false;
}

How can I sort a WinForms DataGridView on a CheckBox column?

So I had a DataGridView with autogenerated columns, some of which were checkbox columns. When I clicked on the check box column's header, it didn't sort. I researched it and it turns out that Microsoft didn't include automatic sorting for checkbox columns... Which I think is absurd--how hard is it to sort checked / not checked?
How can you get a DataGridView to sort check box columns for you?
Here's what I came up with:
You could also simply do this:
DataGridView.Columns("ColumnOfChoice").SortMode = DataGridViewColumnSortMode.Automatic
Works in vb.net 4.0
You only need to add next lines to the code of the form (tested in VB.NET 2013)
Private Sub DataGridView1_ColumnAdded(sender As Object, e As System.Windows.Forms.DataGridViewColumnEventArgs) Handles DataGridView1.ColumnAdded
If e.Column.GetType Is GetType(DataGridViewCheckBoxColumn) Then
e.Column.SortMode = DataGridViewColumnSortMode.Automatic
End If
End Sub
First you need to hook into two events, the column added event and the column header click event:
AddHandler dg.ColumnAdded, AddressOf dgColumnAdded
AddHandler dg.ColumnHeaderMouseClick, AddressOf dgSortColumns
Then, enable programmatic sorting for each check box column:
Private Sub dgColumnAdded(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewColumnEventArgs)
If e.Column.GetType Is GetType(DataGridViewCheckBoxColumn) Then
e.Column.SortMode = DataGridViewColumnSortMode.Programmatic
End If
End Sub
Then, create a handler that will sort a checkbox column, but do nothing for columns that will handle their own sorting:
Private Sub dgSortColumns(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs)
Dim dg As DataGridView = sender
Dim c As DataGridViewColumn = dg.Columns(e.ColumnIndex)
If c.SortMode = DataGridViewColumnSortMode.Programmatic Then
If dg.SortedColumn IsNot Nothing _
AndAlso dg.SortedColumn.Name <> c.Name Then
dg.Sort(c, System.ComponentModel.ListSortDirection.Ascending)
Else
Select Case dg.SortOrder
Case Windows.Forms.SortOrder.None
dg.Sort(c, System.ComponentModel.ListSortDirection.Ascending)
Case Windows.Forms.SortOrder.Ascending
dg.Sort(c, System.ComponentModel.ListSortDirection.Descending)
Case Windows.Forms.SortOrder.Descending
dg.Sort(c, System.ComponentModel.ListSortDirection.Ascending)
End Select
End If
End If
End Sub
And there you go! Now was it really that hard, Microsoft? ;-)
I'm not sure about VB, but for C# in VS2012 in the designer you can also set the SortMode.
Right-click on the DataGridView and go to "Edit Columns".
There's a drop-down for SortMode with a choice of NotSortable, Automatic, and Programmatic.
It appears that the default for most columns is Automatic, but for checkboxes (boolean) columns the default is NotSortable.
I have created an extension method that you can reuse, you just need to use it during the form load event.
------ Be sure that your DataSource is sortable. ------
If you are binding the DataGridView to a simple List it WONT WORK, you need to use something else, I recommend you to use this SortableBindingList; You can pass directly your original List IEnumerable to the SortableBindingList's constructor.
Load:
private void frmWithTheDataGrid_Load(object sender, EventArgs e)
{
this.yourDataGridView.SortabilizeMe();
}
Then add this into a static class to use it as an ExtensionMethod..
public static void SortabilizeMe(this DataGridView dgv)
{
dgv.ColumnAdded+= delegate(object sender, DataGridViewColumnEventArgs args)
{
args.Column.SortMode = DataGridViewColumnSortMode.Programmatic;
};
dgv.ColumnHeaderMouseClick += delegate(object sender, DataGridViewCellMouseEventArgs args)
{
var col = dgv.Columns[args.ColumnIndex];
if (dgv.SortedColumn != null && dgv.SortedColumn.Name != col.Name)
{
dgv.Sort(row, ListSortDirection.Ascending);
}
else
{
switch (dgv.SortOrder)
{
case SortOrder.None:
dgv.Sort(col, ListSortDirection.Ascending);
break;
case SortOrder.Ascending:
dgv.Sort(col, ListSortDirection.Descending);
break;
case SortOrder.Descending:
dgv.Sort(col, ListSortDirection.Ascending);
break;
}
}
};
}
Then magic will happen :)

How to require CheckedListBox to have at least one item selected

With the CheckListBox in VB.NET in VS2005, how would you make it compulsory that at least one item is selected?
Can you select one of the items at design time to make it the default?
What would be best way to handle the validation part of this scenario? When should the user be required to tick one of the boxes?
Either idea -- preventing the user from unchecking the last checked item, or validating that at least one item is checked before proceeding -- is fairly straightforward to implement.
How to prevent the user from unchecking the last checked item
1. Make sure at least one item is checked to begin with (e.g., in your form's Load event):
Private Sub frm_Load(ByVal sender As Object, ByVal e As EventArgs)
clb.SetItemChecked(0, True) ' whatever index you want as your default '
End Sub
2. Add some simple logic to your ItemCheck event handler:
Private Sub clb_ItemCheck(ByVal sender As Object, ByVal e As ItemCheckEventArgs)
If clb.CheckedItems.Count = 1 Then ' only one item is still checked... '
If e.CurrentValue = CheckState.Checked Then ' ...and this is it... '
e.NewValue = CheckState.Checked ' ...so keep it checked '
End If
End If
End Sub
How to validate that at least one item is checked
Private Sub btn_Click(ByVal sender As Object, ByVal e As EventArgs)
' you could also put the below in its own method '
If clb.CheckedItems.Count < 1 Then
MsgBox("You must check at least one item.")
Return
End If
' do whatever you need to do '
End Sub
You can, but the user can always uncheck it.
The way I would do this would be on submit, loop through the checkbox items and make sure at least one of them was checked (break the loop after as your requirement is met, no need to process the rest of the list.)
If the check fails, make a custom validator visible. Required field validators don't work with Check List Boxes, but you can see how they implemented here:
http://www.codeproject.com/KB/webforms/Validator_CheckBoxList.aspx
This solution should be close. Not 100%, there is no easy way to find out that items were added to an empty list. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of your toolbox onto your form. No additional code is needed.
Public Class MyCheckedListBox
Inherits CheckedListBox
Protected Overrides Sub OnEnter(ByVal e As System.EventArgs)
REM Ensure at least one item is checked
MyBase.OnEnter(e)
If Me.CheckedIndices.Count = 0 AndAlso Me.Items.Count > 0 Then
Me.SetItemChecked(0, True)
End If
End Sub
Protected Overrides Sub OnItemCheck(ByVal e As ItemCheckEventArgs)
REM Prevent unchecking last item
If Me.CheckedIndices.Count <= 1 AndAlso e.NewValue = CheckState.Unchecked Then e.NewValue = CheckState.Checked
MyBase.OnItemCheck(e)
End Sub
End Class
Optional additional override that ensures an item is checked when the form first shows up:
Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs)
MyBase.OnHandleCreated(e)
If Me.CheckedIndices.Count = 0 AndAlso Me.Items.Count > 0 Then
Me.SetItemChecked(0, True)
End If
End Sub
Trap ItemCheck event and verify if last checkbox is unchecked:
private void Form1_Load(object sender, EventArgs e)
{
checkedListBox1.SetItemChecked(0, true);
checkedListBox1.ItemCheck += checkedListBox1_ItemCheck;
}
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (CountChecked() == 1 &&
e.NewValue == CheckState.Unchecked &&
e.CurrentValue == CheckState.Checked)
{
checkedListBox1.SetItemChecked(0, true);
}
}
private int CountChecked()
{
int count = 0;
for (int i = 0; i < checkedListBox1.Items.Count; i++)
{
if (checkedListBox1.GetItemChecked(i) == true)
count++;
}
return count;
}
Updated: Then you have to make async call to set item check state back.
private delegate void SetItemCallback(int index);
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (checkedListBox1.CheckedIndices.Count == 1 &&
e.NewValue == CheckState.Unchecked &&
e.CurrentValue == CheckState.Checked)
{
int index = checkedListBox1.CheckedIndices[0];
// Initiate the asynchronous call.
SetItemCallback d = new SetItemCallback(this.SetItem);
d.BeginInvoke(index, null, null);
}
}
private void SetItem(int index)
{
if (this.checkedListBox1.InvokeRequired)
{
SetItemCallback d = new SetItemCallback(SetItem);
this.Invoke(d, new object[] { index });
}
else
{
checkedListBox1.SetItemChecked(index, true);
}
}
Validation Scenario Suggestion :
If your winform has an Submit/Save button, I would like the application to show an error when the button is cliked (provided there is a label saying that there should be atleast one item selected). The error message need not be a MessageBox, it could a red label showing the error description in the form itself
If there are no buttons you can hold on to, then do the validation as when users click on the items. Using ItemCheck event, check if there is atleast one item checked. If not, show the error label.
Code :
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (checkedListBox1.CheckedIndices.Count == 1 &&
e.NewValue == CheckState.Unchecked &&
e.CurrentValue == CheckState.Checked)
{
//Show error label in red
}
}
I would use the ItemCheck event to set the Button.Enabled = false when there is not at least one item checked, and Button.Enabled = true when there is at least one item checked (assuming an Ok button or something alike).