Enable/Disable selectAll check box in Infragistics UltraWinGrid - vb.net

I have used Infragistics UltraWinGrid to display data on the Grid. In this grid, there is one check box column. I have added check box in header in this column to selectAll option.
Now I want to enable/disable this header check box on any button click event to restrict user to perform any action.
Can any one tell me how to do this?
Thanks in advance.

I can think of two options off the top:
1) If you don't want the user to click any of the checkboxes, just hide the entire column if they are not authorized.
2) If you only want to keep the user from selecting all of the items in the grid, add code to the selectAll method to ignore the request if the user is not authorized.
Update
3) If you are using a version of the grid that supports it, you can use:
grid.DisplayLayout.Override.HeaderCheckBoxVisibility = HeaderCheckBoxVisibility.Never
when the form containing the grid is loaded if the user is not authorized.

The check box in the header is provided by a HeaderCheckBoxUIElement and this has an enabled property that can be set to determine if the check box is enabled. To get a reference to the HeaderCheckBoxUIElement you can use the MouseEnterElement and set the Enabled property in that event. For tracking if it is enabled you could use the Tag property of the column.
The code in VB:
Private Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
Dim col As UltraGridColumn = Me.UltraGrid1.DisplayLayout.Bands(0).Columns("OnSite")
col.Tag = (TypeOf col.Tag Is Boolean AndAlso CBool(col.Tag) = False)
End Sub
Private Sub UltraGrid1_MouseEnterElement(sender As Object, e As Infragistics.Win.UIElementEventArgs) Handles UltraGrid1.MouseEnterElement
If TypeOf e.Element Is HeaderCheckBoxUIElement Then
Dim element As HeaderCheckBoxUIElement = DirectCast(e.Element, HeaderCheckBoxUIElement)
element.Enabled = (TypeOf element.Column.Tag Is Boolean AndAlso CBool(element.Column.Tag) = True)
End If
End Sub
The code in C#:
void ultraGrid1_MouseEnterElement(object sender, UIElementEventArgs e)
{
if (e.Element is HeaderCheckBoxUIElement)
{
HeaderCheckBoxUIElement element = (HeaderCheckBoxUIElement)e.Element;
element.Enabled = (element.Column.Tag is bool && (bool)element.Column.Tag == true);
}
}
private void button1_Click(object sender, EventArgs e)
{
UltraGridColumn col = this.ultraGrid1.DisplayLayout.Bands[0].Columns["OnSite"];
col.Tag = (col.Tag is bool && (bool)col.Tag == false);
}

Related

Multiple Listboxes, One Selection

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.

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).

How to set tooltips on ListView Subitems in .Net

I am trying to set the tool tip text for some of my subitems in my listview control. I am unable to get the tool tip to show up.
Anyone have any suggestions?
Private _timer As Timer
Private Sub Timer()
If _timer Is Nothing Then
_timer = New Timer
_timer.Interval = 500
AddHandler _timer.Tick, AddressOf TimerTick
_timer.Start()
End If
End Sub
Private Sub TimerTick(ByVal sender As Object, ByVal e As EventArgs)
_timer.Enabled = False
End Sub
Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
If Not _timer.Enabled Then
Dim item = Me.HitTest(e.X, e.Y)
If Not item Is Nothing AndAlso Not item.SubItem Is Nothing Then
If item.SubItem.Text = "" Then
Dim tip = New ToolTip
Dim p = item.SubItem.Bounds
tip.ToolTipTitle = "Status"
tip.ShowAlways = True
tip.Show("FOO", Me, e.X, e.Y, 1000)
_timer.Enabled = True
End If
End If
End If
MyBase.OnMouseMove(e)
End Sub
You can use the MouseMove event:
private void listview1_MouseMove(object sender, MouseEventargs e)
{
ListViewItem item = listview1.GetItemAt(e.X, e.Y);
ListViewHitTestInfo info = listview1.HitTest(e.X, e.Y);
if((item != null) && (info.SubItem != null))
{
toolTip1.SetToolTip(listview1, info.SubItem.Text);
}
else
{
toolTip1.SetToolTip(listview1, "");
}
}
Assuming .NET 2.0 or later, you can also set ListView.ShowItemToolTips to true. If you need to customize the tooltip text for a given item, set ListViewItem.ToolTipText to the string you want displayed.
ObjectListView (an open source wrapper around .NET WinForms ListView) has builtin support for cell tooltips (and, yes, it does work with VB). You listen for a CellToolTip event and you can do things like this (which is admittedly excessive):
If you don't want to use ObjectListView, you need to subclass ListView, listen for WM_NOTIFY messages, and then within those, respond to TTN_GETDISPINFO notifications, in a manner similar to this:
case TTN_GETDISPINFO:
ListViewHitTestInfo info = this.HitTest(this.PointToClient(Cursor.Position));
if (info.Item != null && info.SubItem != null) {
// Call some method of your own to get the tooltip you want
String tip = this.GetCellToolTip(info.Item, info.SubItem);
if (!String.IsNullOrEmpty(tip)) {
NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT)m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT));
ttt.lpszText = tip;
if (this.RightToLeft == RightToLeft.Yes)
ttt.uFlags |= 4;
Marshal.StructureToPtr(ttt, m.LParam, false);
return; // do not do normal processing
}
}
break;
Obviously, this is C#, not VB, but you get the idea.
The original code in the question does not work since it creates a New ToolTip inside OnMouseMove. I guess that the ToolTip.Show method is asynchronous and thus the function exits immediately after invoking it, destroying the temporary ToolTip. When Show gets to execute, the object does not exist anymore.
The solution would be to create a persistent ToolTip object, by:
a ToolTip control on the form; or
a private ToolTip class field (disposed in the Finalize or Dispose method of the class); or
a Static object inside the function.
Also, there is no need to GetItemAt() since ListViewHitTestInfo already contains both the item and subitem references.
Improving Colin's answer, here is my code:
Private Sub ListView_MouseMove(sender As Object, e As MouseEventArgs) _
Handles MyList1.MouseMove
Static prevMousePos As Point = New Point(-1, -1)
Dim lv As ListView = TryCast(sender, ListView)
If lv Is Nothing Then _
Exit Sub
If prevMousePos = MousePosition Then _
Exit Sub ' to avoid annoying flickering
With lv.HitTest(lv.PointToClient(MousePosition))
If .SubItem IsNot Nothing AndAlso Not String.IsNullOrEmpty(.SubItem.Text) Then
'AndAlso .Item.SubItems.IndexOf(.SubItem) = 1
'...when a specific Column is needed
Static t As ToolTip = toolTip1 ' using a form's control
'Static t As New ToolTip() ' using a private variable
t.ShowAlways = True
t.UseFading = True
' To display at exact mouse position:
t.Show(.SubItem.Tag, .Item.ListView, _
.Item.ListView.PointToClient(MousePosition), 2000)
' To display beneath the list subitem:
t.Show(.SubItem.Tag, .Item.ListView, _
.SubItem.Bounds.Location + New Size(7, .SubItem.Bounds.Height + 1), 2000)
' To display beneath mouse cursor, as Windows does:
' (size is hardcoded in ugly manner because there is no easy way to find it)
t.Show(.SubItem.Tag, .Item.ListView, _
.Item.ListView.PointToClient(Cursor.Position + New Size(1, 20)), 2000)
End If
prevMousePos = MousePosition
End With
End Sub
I've made the code as general as possible so that the function could be assigned to multiple ListViews.
If you set ShowItemTooltips for the ListView control in "details" mode and do nothing else, the ListView control will automatically provide tooltips for items and subitems that exceed their column's widths. This turns out to work even if the FullRowSelect property is set to true. If ToolTipText has been set for a ListViewItem and FullRowSelect is true, then the tooltip will appear for the whole row; that's the case where tooltips won't be displayed for subitems.