1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 // (c) 2008-2011 Moz (@syo68k)
4 // (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 // (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 // (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 // (c) 2011 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
8 // All rights reserved.
10 // This file is part of OpenTween.
12 // This program is free software; you can redistribute it and/or modify it
13 // under the terms of the GNU General Public License as published by the Free
14 // Software Foundation; either version 3 of the License, or (at your option)
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25 // Boston, MA 02110-1301, USA.
30 using System.Collections.Generic;
33 using System.Windows.Forms;
35 using System.ComponentModel;
36 using System.Runtime.InteropServices;
37 using System.Diagnostics;
39 namespace OpenTween.OpenTweenCustomControl
41 public sealed class DetailsListView : ListView
43 private Rectangle changeBounds;
45 public ContextMenuStrip? ColumnHeaderContextMenuStrip { get; set; }
47 public event EventHandler VScrolled;
48 public event EventHandler HScrolled;
50 public DetailsListView()
54 HideSelection = false;
55 DoubleBuffered = true;
58 //[System.ComponentModel.DefaultValue(0),
59 // System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.Repaint)]
60 //public new int VirtualListSize
62 // get { return base.VirtualListSize; }
65 // if (value == base.VirtualListSize) return;
66 // if (base.VirtualListSize > 0 && value > 0)
69 // if (!this.IsDisposed)
71 // if (base.VirtualListSize < value)
73 // if (this.TopItem == null)
79 // topIndex = this.TopItem.Index;
81 // topIndex = Math.Min(topIndex, Math.Abs(value - 1));
82 // this.TopItem = this.Items[topIndex];
86 // if (this.TopItem == null)
94 // this.TopItem = this.Items[0];
98 // base.VirtualListSize = value;
103 /// 複数選択時の起点になるアイテム (selection mark) の位置を取得・設定する
106 /// Items[idx].Selected の設定では mark が設定されるが、SelectedIndices.Add(idx) では設定されないため、
109 public int SelectionMark
111 get => NativeMethods.ListView_GetSelectionMark(this.Handle);
112 set => NativeMethods.ListView_SetSelectionMark(this.Handle, value);
115 public void SelectItems(int[] indices)
117 foreach (var index in indices)
119 if (index < 0 || index >= this.VirtualListSize)
120 throw new ArgumentOutOfRangeException(nameof(indices));
122 NativeMethods.SelectItem(this, index);
125 this.OnSelectedIndexChanged(EventArgs.Empty);
128 public void SelectAllItems()
130 NativeMethods.SelectAllItems(this);
132 this.OnSelectedIndexChanged(EventArgs.Empty);
135 public void ChangeItemBackColor(ListViewItem item, Color backColor)
137 if (item.BackColor == backColor)
140 item.BackColor = backColor;
141 this.RefreshItemBounds(item);
144 public void ChangeItemForeColor(ListViewItem item, Color foreColor)
146 if (item.ForeColor == foreColor)
149 item.ForeColor = foreColor;
150 this.RefreshItemBounds(item);
153 public void ChangeItemFontAndColor(ListViewItem item, Color foreColor, Font fnt)
155 if (item.ForeColor == foreColor && item.Font == fnt)
158 item.ForeColor = foreColor;
160 this.RefreshItemBounds(item);
163 private void RefreshItemBounds(ListViewItem item)
167 var itemBounds = item.Bounds;
168 var drawBounds = Rectangle.Intersect(this.ClientRectangle, itemBounds);
169 if (drawBounds == Rectangle.Empty)
172 this.changeBounds = drawBounds;
174 this.changeBounds = Rectangle.Empty;
176 catch (ArgumentException)
178 //タイミングによりBoundsプロパティが取れない?
179 this.changeBounds = Rectangle.Empty;
183 [StructLayout(LayoutKind.Sequential)]
186 public IntPtr hwndFrom;
187 public IntPtr idFrom;
191 [DebuggerStepThrough]
192 protected override void WndProc(ref Message m)
194 const int WM_ERASEBKGND = 0x14;
195 const int WM_PAINT = 0xF;
196 const int WM_MOUSEWHEEL = 0x20A;
197 const int WM_MOUSEHWHEEL = 0x20E;
198 const int WM_HSCROLL = 0x114;
199 const int WM_VSCROLL = 0x115;
200 const int WM_KEYDOWN = 0x100;
201 const int WM_USER = 0x400;
202 const int WM_REFLECT = WM_USER + 0x1C00;
203 const int WM_NOTIFY = 0x004E;
204 const int WM_CONTEXTMENU = 0x7B;
205 const int LVM_SETITEMCOUNT = 0x102F;
206 const int LVN_ODSTATECHANGED = ((0 - 100) - 15);
207 const long LVSICF_NOSCROLL = 0x2;
208 const long LVSICF_NOINVALIDATEALL = 0x1;
216 if (this.changeBounds != Rectangle.Empty)
220 if (this.changeBounds != Rectangle.Empty)
222 NativeMethods.ValidateRect(this.Handle, IntPtr.Zero);
223 this.Invalidate(this.changeBounds);
224 this.changeBounds = Rectangle.Empty;
228 HScrolled?.Invoke(this, EventArgs.Empty);
231 VScrolled?.Invoke(this, EventArgs.Empty);
236 vPos = NativeMethods.GetScrollPosition(this, NativeMethods.ScrollBarDirection.SB_VERT);
237 hPos = NativeMethods.GetScrollPosition(this, NativeMethods.ScrollBarDirection.SB_HORZ);
240 if (m.WParam != this.Handle)
243 this.ColumnHeaderContextMenuStrip?.Show(new Point(m.LParam.ToInt32()));
247 case LVM_SETITEMCOUNT:
248 m.LParam = new IntPtr(LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL);
250 case WM_REFLECT + WM_NOTIFY:
251 var nmhdr = Marshal.PtrToStructure<NMHDR>(m.LParam);
253 // Ctrl+クリックで選択状態を変更した場合にイベントが発生しない問題への対処
254 if (nmhdr.code == LVN_ODSTATECHANGED)
255 this.OnSelectedIndexChanged(EventArgs.Empty);
263 catch (ArgumentOutOfRangeException)
265 //Substringでlengthが0以下。アイコンサイズが影響?
267 catch (AccessViolationException)
271 if (this.IsDisposed) return;
274 if (vPos != NativeMethods.GetScrollPosition(this, NativeMethods.ScrollBarDirection.SB_VERT))
275 VScrolled?.Invoke(this, EventArgs.Empty);
277 if (hPos != NativeMethods.GetScrollPosition(this, NativeMethods.ScrollBarDirection.SB_HORZ))
278 HScrolled?.Invoke(this, EventArgs.Empty);