Reputation: 9
I'm using 3 ListBox Controls and I want to remove their Scrollbars, so they're appearance may looking cleaner.
I have it so when I select an item in any, it selects the same in the rest. Only problem is that I have no idea how to make them scroll together.
E.g., if I scrolled down in the first Listbox, the position of the other two should match the position of the the first one.
I'd also like to know how to remove the Scrollbar, since there is no property for this.
Upvotes: 0
Views: 725
Reputation: 32223
Here's an example of a ListBox stripped of its Vertical ScrollBar that can handle Mouse Wheel messages and scroll itself.
The Vertical ScrollBar is removed by default, unless the ScrollAlwaysVisible property is set to true
or the custom public VerticalScrollBar
property is set to true
.
The LisBox is scrolled setting its TopIndex property. There's a Min/Max check in WndProc where WM_MOUSEWHEEL
is handled that ensures that the list is not scrolled beyond its limits.
It's kind of a redundant check, but may come in handy if you needs to be perform a more complex calculation to determine the current offset.
When the ListBox is scrolled, it raises the custom public Scroll
event. You could create a custom EventArgs class to pass specific values to the Event Handler, if required. Here, I'm just synchronizing all ListBox Controls using the TopIndex
property.
Note that the Mouse Wheel scrolls the ListBox by 1 Item, while also pressing SHIFT sets the scroll to 3 Items. Modify this behavior as required.
using System.ComponentModel;
using System.Windows.Forms;
[DesignerCategory("code")]
public class ListBoxEx : ListBox
{
public event EventHandler<EventArgs> Scroll;
private const int WS_VSCROLL = 0x200000;
private const int WM_MOUSEWHEEL = 0x020A;
private const int MK_SHIFT = 0x0004;
private bool m_VerticalScrollBar = false;
public ListBoxEx() { }
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
if (!ScrollAlwaysVisible && !m_VerticalScrollBar) {
cp.Style &=~WS_VSCROLL;
}
return cp;
}
}
[DefaultValue(false)]
public bool VerticalScrollBar {
get => m_VerticalScrollBar;
set {
if (value != m_VerticalScrollBar) {
m_VerticalScrollBar = value;
RecreateHandle();
}
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg) {
case WM_MOUSEWHEEL:
var wparm = m.WParam.ToInt64();
int button = (short)wparm;
int delta = (int)(wparm >> 16);
int direction = Math.Sign(delta);
int steps = button == MK_SHIFT ? 3 : 1;
TopIndex = Math.Max(Math.Min(Items.Count - 1, TopIndex - (steps * direction)), 0);
Scroll?.Invoke(this, EventArgs.Empty);
m.Result = IntPtr.Zero;
break;
}
}
}
Add three instances of this Custom Control to a Form and subscribe to the SelectedIndexChanged
event of the first one (for example). E.g.,
private void listBoxEx1_SelectedIndexChanged(object sender, EventArgs e)
{
var lb = (sender as ListBox);
if (listBoxEx2.Items.Count > lb.SelectedIndex) {
listBoxEx2.SelectedIndex = lb.SelectedIndex;
}
if (listBoxEx3.Items.Count > lb.SelectedIndex) {
listBoxEx3.SelectedIndex = lb.SelectedIndex;
}
}
Now, if you want to sync-scroll the three Controls, subscribe to the custom Scroll event of the first:
private void listBoxEx1_Scroll(object sender, EventArgs e)
{
var lb = sender as ListBox;
listBoxEx2.TopIndex = lb.TopIndex;
listBoxEx3.TopIndex = lb.TopIndex;
}
This is how it works:
The code sample also handles ListBox Controls with different number of Items
Edit:
How to use:
ListBoxEx
is a Custom Control class.
To create this Control:
Class
file to the Project, name it ListBoxEx
.using
directives you find in this code.Now, in the ToolBox, you can find the new ListBoxEx Control.
To replicate what is shown here:
listBoxEx1
).Scroll
and SelectedIndexChanged
events and double-click each. It will create the event handlers for you.Or, subscribe to the events in code:
Copy the listBoxEx1_Scroll
and listBoxEx1_SelectedIndexChanged
handlers you find here (including their content) and paste them inside the Form that contains the ListBoxEx Controls.
Add this to the Form Constructor, after InitializeComponent()
, to subscribe to the Scroll
and SelectedIndexChanged
events of listBoxEx1
:
listBoxEx1.Scroll += this.listBoxEx1_Scroll;
listBoxEx1.SelectedIndexChanged += this.listBoxEx1_SelectedIndexChanged;
Upvotes: 3