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.
28 using System.Collections.Generic;
31 using System.Windows.Forms;
33 using System.ComponentModel;
34 using System.Runtime.InteropServices;
35 using System.Diagnostics;
37 namespace OpenTween.OpenTweenCustomControl
39 public sealed class DetailsListView : ListView
41 private Rectangle changeBounds;
43 public ContextMenuStrip ColumnHeaderContextMenuStrip { get; set; }
45 public event EventHandler VScrolled;
46 public event EventHandler HScrolled;
48 public DetailsListView()
52 HideSelection = false;
53 DoubleBuffered = true;
56 //[System.ComponentModel.DefaultValue(0),
57 // System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.Repaint)]
58 //public new int VirtualListSize
60 // get { return base.VirtualListSize; }
63 // if (value == base.VirtualListSize) return;
64 // if (base.VirtualListSize > 0 && value > 0)
67 // if (!this.IsDisposed)
69 // if (base.VirtualListSize < value)
71 // if (this.TopItem == null)
77 // topIndex = this.TopItem.Index;
79 // topIndex = Math.Min(topIndex, Math.Abs(value - 1));
80 // this.TopItem = this.Items[topIndex];
84 // if (this.TopItem == null)
92 // this.TopItem = this.Items[0];
96 // base.VirtualListSize = value;
101 /// 複数選択時の起点になるアイテム (selection mark) の位置を取得・設定する
104 /// Items[idx].Selected の設定では mark が設定されるが、SelectedIndices.Add(idx) では設定されないため、
107 public int SelectionMark
109 get => NativeMethods.ListView_GetSelectionMark(this.Handle);
110 set => NativeMethods.ListView_SetSelectionMark(this.Handle, value);
113 public void SelectItems(int[] indices)
115 foreach (var index in indices)
117 if (index < 0 || index >= this.VirtualListSize)
118 throw new ArgumentOutOfRangeException(nameof(indices));
120 NativeMethods.SelectItem(this, index);
123 this.OnSelectedIndexChanged(EventArgs.Empty);
126 public void SelectAllItems()
128 NativeMethods.SelectAllItems(this);
130 this.OnSelectedIndexChanged(EventArgs.Empty);
133 public void ChangeItemBackColor(int index, Color backColor)
134 => this.ChangeSubItemBackColor(index, 0, backColor);
136 public void ChangeItemForeColor(int index, Color foreColor)
137 => this.ChangeSubItemForeColor(index, 0, foreColor);
139 public void ChangeItemFont(int index, Font fnt)
140 => this.ChangeSubItemFont(index, 0, fnt);
142 public void ChangeItemFontAndColor(int index, Color foreColor, Font fnt)
143 => this.ChangeSubItemStyles(index, 0, BackColor, foreColor, fnt);
145 public void ChangeItemStyles(int index, Color backColor, Color foreColor, Font fnt)
146 => this.ChangeSubItemStyles(index, 0, backColor, foreColor, fnt);
148 public void ChangeSubItemBackColor(int itemIndex, int subitemIndex, Color backColor)
150 var item = this.Items[itemIndex];
151 item.SubItems[subitemIndex].BackColor = backColor;
152 SetUpdateBounds(item, subitemIndex);
154 this.changeBounds = Rectangle.Empty;
157 public void ChangeSubItemForeColor(int itemIndex, int subitemIndex, Color foreColor)
159 var item = this.Items[itemIndex];
160 item.SubItems[subitemIndex].ForeColor = foreColor;
161 SetUpdateBounds(item, subitemIndex);
163 this.changeBounds = Rectangle.Empty;
166 public void ChangeSubItemFont(int itemIndex, int subitemIndex, Font fnt)
168 var item = this.Items[itemIndex];
169 item.SubItems[subitemIndex].Font = fnt;
170 SetUpdateBounds(item, subitemIndex);
172 this.changeBounds = Rectangle.Empty;
175 public void ChangeSubItemFontAndColor(int itemIndex, int subitemIndex, Color foreColor, Font fnt)
177 var item = this.Items[itemIndex];
178 var subItem = item.SubItems[subitemIndex];
179 subItem.ForeColor = foreColor;
181 SetUpdateBounds(item, subitemIndex);
183 this.changeBounds = Rectangle.Empty;
186 public void ChangeSubItemStyles(int itemIndex, int subitemIndex, Color backColor, Color foreColor, Font fnt)
188 var item = this.Items[itemIndex];
189 var subItem = item.SubItems[subitemIndex];
190 subItem.BackColor = backColor;
191 subItem.ForeColor = foreColor;
193 SetUpdateBounds(item, subitemIndex);
195 this.changeBounds = Rectangle.Empty;
198 private void SetUpdateBounds(ListViewItem item, int subItemIndex)
202 if (subItemIndex > this.Columns.Count)
204 throw new ArgumentOutOfRangeException(nameof(subItemIndex));
206 if (item.UseItemStyleForSubItems)
208 this.changeBounds = item.Bounds;
212 this.changeBounds = this.GetSubItemBounds(item, subItemIndex);
215 catch (ArgumentException)
217 //タイミングによりBoundsプロパティが取れない?
218 this.changeBounds = Rectangle.Empty;
222 private Rectangle GetSubItemBounds(ListViewItem item, int subitemIndex)
224 if (subitemIndex == 0 && this.Columns.Count > 0)
226 Rectangle col0 = item.Bounds;
227 return new Rectangle(col0.Left, col0.Top, item.SubItems[1].Bounds.X + 1, col0.Height);
231 return item.SubItems[subitemIndex].Bounds;
235 [StructLayout(LayoutKind.Sequential)]
238 public IntPtr hwndFrom;
239 public IntPtr idFrom;
243 [DebuggerStepThrough]
244 protected override void WndProc(ref Message m)
246 const int WM_ERASEBKGND = 0x14;
247 const int WM_PAINT = 0xF;
248 const int WM_MOUSEWHEEL = 0x20A;
249 const int WM_MOUSEHWHEEL = 0x20E;
250 const int WM_HSCROLL = 0x114;
251 const int WM_VSCROLL = 0x115;
252 const int WM_KEYDOWN = 0x100;
253 const int WM_USER = 0x400;
254 const int WM_REFLECT = WM_USER + 0x1C00;
255 const int WM_NOTIFY = 0x004E;
256 const int WM_CONTEXTMENU = 0x7B;
257 const int LVM_SETITEMCOUNT = 0x102F;
258 const int LVN_ODSTATECHANGED = ((0 - 100) - 15);
259 const long LVSICF_NOSCROLL = 0x2;
260 const long LVSICF_NOINVALIDATEALL = 0x1;
268 if (this.changeBounds != Rectangle.Empty)
272 if (this.changeBounds != Rectangle.Empty)
274 NativeMethods.ValidateRect(this.Handle, IntPtr.Zero);
275 this.Invalidate(this.changeBounds);
276 this.changeBounds = Rectangle.Empty;
280 HScrolled?.Invoke(this, EventArgs.Empty);
283 VScrolled?.Invoke(this, EventArgs.Empty);
288 vPos = NativeMethods.GetScrollPosition(this, NativeMethods.ScrollBarDirection.SB_VERT);
289 hPos = NativeMethods.GetScrollPosition(this, NativeMethods.ScrollBarDirection.SB_HORZ);
292 if (m.WParam != this.Handle)
295 this.ColumnHeaderContextMenuStrip?.Show(new Point(m.LParam.ToInt32()));
299 case LVM_SETITEMCOUNT:
300 m.LParam = new IntPtr(LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL);
302 case WM_REFLECT + WM_NOTIFY:
303 var nmhdr = Marshal.PtrToStructure<NMHDR>(m.LParam);
305 // Ctrl+クリックで選択状態を変更した場合にイベントが発生しない問題への対処
306 if (nmhdr.code == LVN_ODSTATECHANGED)
307 this.OnSelectedIndexChanged(EventArgs.Empty);
315 catch (ArgumentOutOfRangeException)
317 //Substringでlengthが0以下。アイコンサイズが影響?
319 catch (AccessViolationException)
323 if (this.IsDisposed) return;
326 if (vPos != NativeMethods.GetScrollPosition(this, NativeMethods.ScrollBarDirection.SB_VERT))
327 VScrolled?.Invoke(this, EventArgs.Empty);
329 if (hPos != NativeMethods.GetScrollPosition(this, NativeMethods.ScrollBarDirection.SB_HORZ))
330 HScrolled?.Invoke(this, EventArgs.Empty);