using System.Windows.Input; namespace System.Windows.Forms { public partial class ListBox : ListControl { public const int NoMatches = -1; protected new CustomControls.OwnerDrawListBox control { get; } public SelectedObjectCollection SelectedItems { get; } private SelectedIndexCollection selectedIndices; public SelectedIndexCollection SelectedIndices { get { if (selectedIndices == null) BuildSelectedIndices(); return selectedIndices; } } public int ItemHeight { get => (int)(control.ItemHeight * DpiScale); set => control.ItemHeight = (int)(value / DpiScale); } private DrawMode drawMode; public DrawMode DrawMode { get => drawMode; set { drawMode = value; control.OwnerDraw = value != DrawMode.Normal; } } private SelectionMode selectionMode; public SelectionMode SelectionMode { get => selectionMode; set { selectionMode = value; control.SelectionMode = value switch { SelectionMode.MultiSimple => Windows.Controls.SelectionMode.Multiple, SelectionMode.MultiExtended => Windows.Controls.SelectionMode.Extended, _ => Windows.Controls.SelectionMode.Single }; } } public int TopIndex { get => SelectedIndex; set { if (value != -1) control.ScrollIntoView(Items[value]); } } private bool horizontalScrollbar; public bool HorizontalScrollbar { get => horizontalScrollbar; set { horizontalScrollbar = value; control.SetValue(Windows.Controls.ScrollViewer.HorizontalScrollBarVisibilityProperty, value ? Windows.Controls.ScrollBarVisibility.Visible : Windows.Controls.ScrollBarVisibility.Hidden); } } // these 3 are ignored public bool UseTabStops { get; set; } public int ColumnWidth { get; set; } public bool MultiColumn { get; set; } public event DrawItemEventHandler DrawItem; public event MeasureItemEventHandler MeasureItem; private void ListBox_MouseDown(object sender, MouseButtonEventArgs e) { var element = control.InputHitTest(e.GetPosition(control)); if (element is FrameworkElement itm && itm.DataContext != null) { // fire event when clicked on the item (otherwise it will be skipped) OnMouseDown(Helper.GetMouseEventArgs(control, e)); } } private void BuildSelectedIndices() { selectedIndices = new SelectedIndexCollection(this); for (int i = 0; i < control.SelectedItems.Count; i++) { var item = control.SelectedItems[i]; selectedIndices.AddInternal(control.Items.IndexOf(item)); } } protected virtual void OnDrawItem(DrawItemEventArgs e) => DrawItem?.Invoke(this, e); protected virtual void OnMeasureItem(MeasureItemEventArgs e) => MeasureItem?.Invoke(this, e); protected override void OnSelectionChanged(EventArgs e) { base.OnSelectionChanged(e); control.ScrollIntoView(SelectedItem); selectedIndices = null; } public void ClearSelected() { control.SelectedItems.Clear(); } public void SetSelected(int index, bool selected) { var item = Items[index]; if (selected) control.SelectedItems.Add(item); else control.SelectedItems.Remove(item); } public int IndexFromPoint(int x, int y) { object item = null; var element = control.InputHitTest(new Point(x / DpiScale, y / DpiScale)); if (element is FrameworkElement itm) { item = itm.DataContext; } return item == null ? -1 : Items.IndexOf(item); } public int IndexFromPoint(Drawing.Point pos) => IndexFromPoint(pos.X, pos.Y); public ListBox() { control = new(); SetControl(control); SelectedItems = new SelectedObjectCollection(control.SelectedItems); control.DrawItem += (sender, e) => OnDrawItem(e); control.MeasureItem += (sender, e) => OnMeasureItem(e); // use this instead of PreviewMouseDown! do not allow unhandled event to bubble/tunnel/whatever control.AddHandler(UIElement.MouseDownEvent, new MouseButtonEventHandler(ListBox_MouseDown), true); // std Control code does not work for these events control.MouseDoubleClick += (s, e) => { OnMouseDoubleClick(Helper.GetMouseEventArgs(control, e)); OnDoubleClick(EventArgs.Empty); }; HorizontalScrollbar = false; Width = 120; Height = 96; } } }