/// <summary> /// Gets the adjacent item to the given item in the given direction. /// If that item is disabled, continue in that direction until an enabled item is found. /// </summary> /// <param name="olvi">The row whose neighbour is sought</param> /// <param name="up">The direction of the adjacentness</param> /// <returns>An OLVListView adjacent to the given item, or null if there are no more enabled items in that direction.</returns> protected OLVListItem GetAdjacentItemOrNull(OLVListItem olvi, bool up) { OLVListItem item = up ? this.ListView.GetPreviousItem(olvi) : this.ListView.GetNextItem(olvi); while (item != null && !item.Enabled) { item = up ? this.ListView.GetPreviousItem(item) : this.ListView.GetNextItem(item); } return(item); }
/// <summary> /// Create a DataObject when the user does a left mouse drag operation. /// See IDragSource for further information. /// </summary> /// <param name="olv"></param> /// <param name="button"></param> /// <param name="item"></param> /// <returns></returns> public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) { // We only drag on left mouse if (button != MouseButtons.Left) { return(null); } return(this.CreateDataObject(olv)); }
/// <summary> /// Start editing the indicated cell if that cell is not already being edited /// </summary> /// <param name="olvi">The row to edit</param> /// <param name="subItemIndex">The cell within that row to edit</param> protected void StartCellEditIfDifferent(OLVListItem olvi, int subItemIndex) { if (this.ItemBeingEdited == olvi && this.SubItemIndexBeingEdited == subItemIndex) { return; } this.ListView.EnsureVisible(olvi.Index); this.ListView.StartCellEdit(olvi, subItemIndex); }
/// <summary> /// Given the item and the subitem, calculate its bounds. /// </summary> /// <param name="item"></param> /// <param name="subItem"></param> /// <returns></returns> public virtual Rectangle CalculateItemBounds(OLVListItem item, OLVListSubItem subItem) { if (item == null) { return(Rectangle.Empty); } if (subItem == null) { return(item.Bounds); } return(item.GetSubItemBounds(item.SubItems.IndexOf(subItem))); }
private static ListViewHitTestLocations ConvertNativeFlagsToDotNetLocation(OLVListItem hitItem, int flags) { // Untangle base .NET behaviour. // In Windows SDK, the value 8 can have two meanings here: LVHT_ONITEMSTATEICON or LVHT_ABOVE. // .NET changes these to be: // - LVHT_ABOVE becomes ListViewHitTestLocations.AboveClientArea (which is 0x100). // - LVHT_ONITEMSTATEICON becomes ListViewHitTestLocations.StateImage (which is 0x200). // So, if we see the 8 bit set in flags, we change that to either a state image hit // (if we hit an item) or to AboveClientAream if nothing was hit. if ((8 & flags) == 8) { return((ListViewHitTestLocations)(0xf7 & flags | (hitItem == null ? 0x100 : 0x200))); } // Mask off the LVHT_EX_XXXX values since ListViewHitTestLocations doesn't have them return((ListViewHitTestLocations)(flags & 0xffff)); }
/// <summary> /// Draw a slight colouring over our tinted column /// </summary> /// <remarks> /// This overlay only works when: /// - the list is in Details view /// - there is at least one row /// - there is a selected column (or a specified tint column) /// </remarks> /// <param name="olv"></param> /// <param name="g"></param> /// <param name="r"></param> public override void Draw(ObjectListView olv, Graphics g, Rectangle r) { if (olv.View != System.Windows.Forms.View.Details) { return; } if (olv.GetItemCount() == 0) { return; } OLVColumn column = this.ColumnToTint ?? olv.SelectedColumn; if (column == null) { return; } Point sides = NativeMethods.GetScrolledColumnSides(olv, column.Index); if (sides.X == -1) { return; } Rectangle columnBounds = new Rectangle(sides.X, r.Top, sides.Y - sides.X, r.Bottom); // Find the bottom of the last item. The tinting should extend only to there. OLVListItem lastItem = olv.GetLastItemInDisplayOrder(); if (lastItem != null) { Rectangle lastItemBounds = lastItem.Bounds; if (!lastItemBounds.IsEmpty && lastItemBounds.Bottom < columnBounds.Bottom) { columnBounds.Height = lastItemBounds.Bottom - columnBounds.Top; } } g.FillRectangle(this.tintBrush, columnBounds); }
/// <summary> /// Create a OlvListViewHitTestInfo /// </summary> public OlvListViewHitTestInfo(OLVListItem olvListItem, OLVListSubItem subItem, int flags, OLVGroup group, int iColumn) { this.item = olvListItem; this.subItem = subItem; this.location = ConvertNativeFlagsToDotNetLocation(olvListItem, flags); this.HitTestLocationEx = (HitTestLocationEx)flags; this.Group = group; this.ColumnIndex = iColumn; this.ListView = olvListItem == null ? null : (ObjectListView)olvListItem.ListView; switch (location) { case ListViewHitTestLocations.StateImage: this.HitTestLocation = HitTestLocation.CheckBox; break; case ListViewHitTestLocations.Image: this.HitTestLocation = HitTestLocation.Image; break; case ListViewHitTestLocations.Label: this.HitTestLocation = HitTestLocation.Text; break; default: if ((this.HitTestLocationEx & HitTestLocationEx.LVHT_EX_GROUP_COLLAPSE) == HitTestLocationEx.LVHT_EX_GROUP_COLLAPSE) { this.HitTestLocation = HitTestLocation.GroupExpander; } else if ((this.HitTestLocationEx & HitTestLocationEx.LVHT_EX_GROUP_MINUS_FOOTER_AND_BKGRD) != 0) { this.HitTestLocation = HitTestLocation.Group; } else { this.HitTestLocation = HitTestLocation.Nothing; } break; } }
/// <summary> /// See IDragSource documentation /// </summary> /// <param name="olv"></param> /// <param name="button"></param> /// <param name="item"></param> /// <returns></returns> public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) { return(null); }
/// <summary> /// Calculate the edit rect /// </summary> /// <param name="g"></param> /// <param name="cellBounds"></param> /// <param name="item"></param> /// <param name="subItemIndex"></param> /// <param name="preferredSize"> </param> /// <returns></returns> protected override Rectangle HandleGetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex, Size preferredSize) { return(this.StandardGetEditRectangle(g, cellBounds, preferredSize)); }
/// <summary> /// Gets the adjacent item to the given item in the given direction, wrapping if needed. /// </summary> /// <param name="olvi">The row whose neighbour is sought</param> /// <param name="up">The direction of the adjacentness</param> /// <returns>An OLVListView adjacent to the given item, or null if there are no more items in that direction.</returns> protected OLVListItem GetAdjacentItem(OLVListItem olvi, bool up) { return(this.GetAdjacentItemOrNull(olvi, up) ?? this.GetAdjacentItemOrNull(null, up)); }
/// <summary> /// Handle a change column command /// </summary> /// <param name="keyData"></param> /// <param name="behaviour"></param> protected virtual void HandleColumnChange(Keys keyData, CellEditCharacterBehaviour behaviour) { // If we couldn't finish editing the current cell, don't try to move it if (!this.ListView.PossibleFinishCellEditing()) { return; } // Changing columns only works in details mode if (this.ListView.View != View.Details) { return; } List <OLVColumn> editableColumns = this.EditableColumnsInDisplayOrder; OLVListItem olvi = this.ItemBeingEdited; int displayIndex = Math.Max(0, editableColumns.IndexOf(this.ListView.GetColumn(this.SubItemIndexBeingEdited))); bool isGoingLeft = behaviour == CellEditCharacterBehaviour.ChangeColumnLeft; // Are we trying to continue past one of the edges? if ((isGoingLeft && displayIndex == 0) || (!isGoingLeft && displayIndex == editableColumns.Count - 1)) { // Yes, so figure out our at edge behaviour CellEditAtEdgeBehaviour atEdgeBehaviour; if (!this.CellEditKeyAtEdgeBehaviourMap.TryGetValue(keyData, out atEdgeBehaviour)) { atEdgeBehaviour = CellEditAtEdgeBehaviour.Wrap; } switch (atEdgeBehaviour) { case CellEditAtEdgeBehaviour.Ignore: return; case CellEditAtEdgeBehaviour.EndEdit: this.HandleEndEdit(); return; case CellEditAtEdgeBehaviour.ChangeRow: case CellEditAtEdgeBehaviour.Wrap: if (atEdgeBehaviour == CellEditAtEdgeBehaviour.ChangeRow) { olvi = GetAdjacentItem(olvi, isGoingLeft && displayIndex == 0); } if (isGoingLeft) { displayIndex = editableColumns.Count - 1; } else { displayIndex = 0; } break; } } else { if (isGoingLeft) { displayIndex -= 1; } else { displayIndex += 1; } } int subItemIndex = editableColumns[displayIndex].Index; this.StartCellEditIfDifferent(olvi, subItemIndex); }
/// <summary> /// Handle a change row command /// </summary> /// <param name="keyData"></param> /// <param name="behaviour"></param> protected virtual void HandleRowChange(Keys keyData, CellEditCharacterBehaviour behaviour) { // If we couldn't finish editing the current cell, don't try to move it if (!this.ListView.PossibleFinishCellEditing()) { return; } OLVListItem olvi = this.ItemBeingEdited; int subItemIndex = this.SubItemIndexBeingEdited; bool isGoingUp = behaviour == CellEditCharacterBehaviour.ChangeRowUp; // Try to find a row above (or below) the currently edited cell // If we find one, start editing it and we're done. OLVListItem adjacentOlvi = this.GetAdjacentItemOrNull(olvi, isGoingUp); if (adjacentOlvi != null) { this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex); return; } // There is no adjacent row in the direction we want, so we must be on an edge. CellEditAtEdgeBehaviour atEdgeBehaviour; if (!this.CellEditKeyAtEdgeBehaviourMap.TryGetValue(keyData, out atEdgeBehaviour)) { atEdgeBehaviour = CellEditAtEdgeBehaviour.Wrap; } switch (atEdgeBehaviour) { case CellEditAtEdgeBehaviour.Ignore: break; case CellEditAtEdgeBehaviour.EndEdit: this.ListView.PossibleFinishCellEditing(); break; case CellEditAtEdgeBehaviour.Wrap: adjacentOlvi = this.GetAdjacentItemOrNull(null, isGoingUp); this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex); break; case CellEditAtEdgeBehaviour.ChangeColumn: // Figure out the next editable column List <OLVColumn> editableColumnsInDisplayOrder = this.EditableColumnsInDisplayOrder; int displayIndex = Math.Max(0, editableColumnsInDisplayOrder.IndexOf(this.ListView.GetColumn(subItemIndex))); if (isGoingUp) { displayIndex = (editableColumnsInDisplayOrder.Count + displayIndex - 1) % editableColumnsInDisplayOrder.Count; } else { displayIndex = (displayIndex + 1) % editableColumnsInDisplayOrder.Count; } subItemIndex = editableColumnsInDisplayOrder[displayIndex].Index; // Wrap to the next row and start the cell edit adjacentOlvi = this.GetAdjacentItemOrNull(null, isGoingUp); this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex); break; } }