/// <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 = CellEditAtEdgeBehaviour.Wrap; this.CellEditKeyAtEdgeBehaviourMap.TryGetValue(keyData, out atEdgeBehaviour); 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; } }
public override void Draw(ObjectListView olv, Graphics g, Rectangle r) { if ((olv.View == View.Details) && (olv.GetItemCount() != 0)) { OLVColumn column = this.ColumnToTint ?? olv.SelectedColumn; if (column != null) { Point scrolledColumnSides = BrightIdeasSoftware.NativeMethods.GetScrolledColumnSides(olv, column.Index); if (scrolledColumnSides.X != -1) { Rectangle rect = new Rectangle(scrolledColumnSides.X, r.Top, scrolledColumnSides.Y - scrolledColumnSides.X, r.Bottom); OLVListItem lastItemInDisplayOrder = olv.GetLastItemInDisplayOrder(); if (lastItemInDisplayOrder != null) { Rectangle bounds = lastItemInDisplayOrder.Bounds; if (!(bounds.IsEmpty || (bounds.Bottom >= rect.Bottom))) { rect.Height = bounds.Bottom - rect.Top; } } g.FillRectangle(this.tintBrush, rect); } } } }
/// <summary> /// Create a OLVListItem for given row index /// </summary> /// <param name="itemIndex">The index of the row that is needed</param> /// <returns>An OLVListItem</returns> public virtual OLVListItem MakeListViewItem(int itemIndex) { OLVListItem olvi = new OLVListItem(this.GetModelObject(itemIndex)); this.FillInValues(olvi, olvi.RowObject); if (this.UseAlternatingBackColors) { if (this.View == View.Details && itemIndex % 2 == 1) { olvi.BackColor = this.AlternateRowBackColorOrDefault; } else { olvi.BackColor = this.BackColor; } this.CorrectSubItemColors(olvi); olvi.BackColor = Color.IndianRed; } if (this.UseHotItem && this.HotItemIndex == itemIndex) { this.ApplyHotItemStyle(olvi); } this.SetSubItemImages(itemIndex, olvi); return(olvi); }
/// <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> /// Handle a mouse down event /// </summary> /// <param name="e"></param> protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (!this.CheckBoxes) { return; } // Did the user click the state icon? If so, toggle the clicked row. // If the given row is selected, all selected rows are given the same checkedness. ListViewHitTestInfo htInfo = this.HitTest(e.Location); if ((htInfo.Location & ListViewHitTestLocations.StateImage) != 0) { OLVListItem clickedItem = (OLVListItem)htInfo.Item; this.ToggleCheckObject(clickedItem.RowObject); if (clickedItem.Selected) { CheckState state = this.ModelToItem(clickedItem.RowObject).CheckState; foreach (Object x in this.SelectedObjects) { this.SetObjectCheckedness(x, state); } } } }
private static void FormatRow(OLVListItem item) { var installment = (Installment)item.RowObject; if (installment == null) return; if (installment.IsPending) item.BackColor = Color.Orange; if (installment.IsRepaid) item.BackColor = Color.FromArgb(61, 153, 57); if (installment.IsPending || installment.IsRepaid) item.ForeColor = Color.White; }
public virtual object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) { if (button != MouseButtons.Left) { return(null); } return(this.CreateDataObject(olv)); }
protected virtual void HandleRetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) { if (this.lastRetrieveVirtualItemIndex != e.ItemIndex) { this.lastRetrieveVirtualItemIndex = e.ItemIndex; this.lastRetrieveVirtualItem = this.MakeListViewItem(e.ItemIndex); } e.Item = this.lastRetrieveVirtualItem; }
/// <summary> /// Create a OLVListItem for given row index /// </summary> /// <param name="itemIndex">The index of the row that is needed</param> /// <returns>An OLVListItem</returns> public virtual OLVListItem MakeListViewItem(int itemIndex) { OLVListItem olvi = new OLVListItem(GetModelObject(itemIndex)); FillInValues(olvi, olvi.RowObject); PostProcessOneRow(itemIndex, GetDisplayOrderOfItemIndex(itemIndex), olvi); return(olvi); }
/// <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(CreateDataObject(olv)); }
/// <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 an event args /// </summary> /// <param name="column"></param> /// <param name="control"></param> /// <param name="r"></param> /// <param name="item"></param> /// <param name="subItemIndex"></param> public CellEditEventArgs(OLVColumn column, Control control, Rectangle r, OLVListItem item, int subItemIndex) { this.Control = control; this.column = column; this.cellBounds = r; this.listViewItem = item; this.rowObject = item.RowObject; this.subItemIndex = subItemIndex; this.value = column.GetValue(item.RowObject); }
/// <summary> /// Handle the keyboard input to mimic a TreeView. /// </summary> /// <param name="keyData"></param> /// <returns>Was the key press handled?</returns> protected override void OnKeyDown(KeyEventArgs e) { OLVListItem focused = this.FocusedItem as OLVListItem; if (focused == null) { base.OnKeyDown(e); return; } Object modelObject = focused.RowObject; Branch br = this.TreeModel.GetBranch(modelObject); switch (e.KeyCode) { case Keys.Left: // If the branch is expanded, collapse it. If it's collapsed, // select the parent of the branch. if (br.IsExpanded) { this.Collapse(modelObject); } else { if (br.ParentBranch != null && br.ParentBranch.Model != null) { this.SelectObject(br.ParentBranch.Model, true); } } e.Handled = true; break; case Keys.Right: // If the branch is expanded, select the first child. // If it isn't expanded and can be, expand it. if (br.IsExpanded) { if (br.ChildBranches.Count > 0) { this.SelectObject(br.ChildBranches[0].Model, true); } } else { if (br.CanExpand) { this.Expand(modelObject); } } e.Handled = true; break; } base.OnKeyDown(e); }
public override OLVListItem MakeListViewItem(int itemIndex) { OLVListItem item = base.MakeListViewItem(itemIndex); Branch branch = this.TreeModel.GetBranch(item.RowObject); if (branch != null) { item.IndentCount = branch.Level - 1; } return(item); }
/// <summary> /// Create a OLVListItem for given row index /// </summary> /// <param name="itemIndex">The index of the row that is needed</param> /// <returns>An OLVListItem</returns> /// <remarks>This differs from the base method by also setting up the IndentCount property.</remarks> public override OLVListItem MakeListViewItem(int itemIndex) { OLVListItem olvItem = base.MakeListViewItem(itemIndex); Branch br = this.TreeModel.GetBranch(olvItem.RowObject); if (br != null) { olvItem.IndentCount = br.Level; } return(olvItem); }
public Rectangle CalculateItemBounds(OLVListItem item, OLVListSubItem si) { if (item == null) { return(Rectangle.Empty); } if (si == null) { return(item.Bounds); } return(item.GetSubItemBounds(item.SubItems.IndexOf(si))); }
public override Rectangle GetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex) { this.ClearState(); this.ListView = (ObjectListView)item.ListView; this.ListItem = item; this.SubItem = item.GetSubItem(subItemIndex); this.Column = this.ListView.GetColumn(subItemIndex); this.RowObject = item.RowObject; this.IsItemSelected = this.ListItem.Selected; this.Bounds = cellBounds; return(this.HandleGetEditRectangle(g, cellBounds, item, subItemIndex)); }
public virtual OLVListItem MakeListViewItem(int itemIndex) { OLVListItem lvi = new OLVListItem(this.GetModelObject(itemIndex)); this.FillInValues(lvi, lvi.RowObject); this.PostProcessOneRow(itemIndex, this.GetItemIndexInDisplayOrder(itemIndex), lvi); if (this.HotRowIndex == itemIndex) { this.UpdateHotRow(lvi); } return(lvi); }
/// <summary> /// Create a OLVListItem for given row index /// </summary> /// <param name="itemIndex">The index of the row that is needed</param> /// <returns>An OLVListItem</returns> public virtual OLVListItem MakeListViewItem(int itemIndex) { var olvi = new OLVListItem(GetModelObject(itemIndex)); FillInValues(olvi, olvi.RowObject); PostProcessOneRow(itemIndex, GetItemIndexInDisplayOrder(itemIndex), olvi); if (HotRowIndex == itemIndex) { UpdateHotRow(olvi); } return(olvi); }
protected override void OnKeyDown(KeyEventArgs e) { OLVListItem focusedItem = base.FocusedItem as OLVListItem; if (focusedItem == null) { base.OnKeyDown(e); return; } object rowObject = focusedItem.RowObject; Branch branch = this.TreeModel.GetBranch(rowObject); switch (e.KeyCode) { case Keys.Left: if (!branch.IsExpanded) { if ((branch.ParentBranch != null) && (branch.ParentBranch.Model != null)) { this.SelectObject(branch.ParentBranch.Model, true); } break; } this.Collapse(rowObject); break; case Keys.Right: if (!branch.IsExpanded) { if (branch.CanExpand) { this.Expand(rowObject); } } else if (branch.ChildBranches.Count > 0) { this.SelectObject(branch.ChildBranches[0].Model, true); } e.Handled = true; goto Label_0110; default: goto Label_0110; } e.Handled = true; Label_0110: base.OnKeyDown(e); }
protected virtual Rectangle CalculateDropTargetRectangle(OLVListItem item, int subItem) { if (subItem > 0) { return(item.SubItems[subItem].Bounds); } Rectangle rectangle = this.ListView.CalculateCellTextBounds(item, subItem); if (item.IndentCount > 0) { int width = this.ListView.SmallImageSize.Width; rectangle.X += width * item.IndentCount; rectangle.Width -= width * item.IndentCount; } return(rectangle); }
/// <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 = CellEditAtEdgeBehaviour.Wrap; this.CellEditKeyAtEdgeBehaviourMap.TryGetValue(keyData, out atEdgeBehaviour); 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); }
public override void Draw(ObjectListView olv, Graphics g, Rectangle r) { // This overlay only works when: // - the list is in Details view // - there is at least one row // - there is a selected column 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> /// Calculate the target rectangle when the given item (and possible subitem) /// is the target of the drop. /// </summary> /// <param name="item"></param> /// <param name="subItem"></param> /// <returns></returns> protected virtual Rectangle CalculateDropTargetRectangle(OLVListItem item, int subItem) { if (subItem > 0) { return(item.SubItems[subItem].Bounds); } Rectangle r = this.ListView.CalculateCellTextBounds(item, subItem); // Allow for indent if (item.IndentCount > 0) { int indentWidth = this.ListView.SmallImageSize.Width; r.X += (indentWidth * item.IndentCount); r.Width -= (indentWidth * item.IndentCount); } return(r); }
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 decoration over our 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; } if (this.ColumnToDecorate == null) { return; } Point sides = NativeMethods.GetScrolledColumnSides(olv, this.ColumnToDecorate.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 decoration 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; } } // Delegate the drawing of the actual decoration this.DrawDecoration(olv, g, r, columnBounds); }
/// <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 != View.Details) { return; } if (olv.GetItemCount() == 0) { return; } OLVColumn column = ColumnToTint ?? olv.SelectedColumn; if (column == null) { return; } Point sides = NativeMethods.GetScrolledColumnSides(olv, column.Index); if (sides.X == -1) { return; } var 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(tintBrush, columnBounds); }
public OlvListViewHitTestInfo(ListViewHitTestInfo hti) { this.item = (OLVListItem)hti.Item; this.subItem = (OLVListSubItem)hti.SubItem; this.location = hti.Location; switch (hti.Location) { case ListViewHitTestLocations.Image: this.HitTestLocation = BrightIdeasSoftware.HitTestLocation.Image; return; case ListViewHitTestLocations.Label: this.HitTestLocation = BrightIdeasSoftware.HitTestLocation.Text; return; case ListViewHitTestLocations.StateImage: this.HitTestLocation = BrightIdeasSoftware.HitTestLocation.CheckBox; return; } this.HitTestLocation = BrightIdeasSoftware.HitTestLocation.Nothing; }
/// <summary> /// Handle the given mouse down event as a possible attempt to expand/collapse /// a row. Return true if the event was handled. /// </summary> /// <param name="e"></param> /// <returns></returns> protected virtual bool HandlePossibleExpandClick(OLVListItem olvItem, MouseEventArgs e) { Branch br = this.TreeModel.GetBranch(olvItem.RowObject); if (br == null || !br.CanExpand) { return(false); } // Calculate if they clicked on the expand/collapse icon Rectangle r = this.GetItemRect(olvItem.Index, ItemBoundsPortion.Icon); r.X = this.CalculateExpanderIndentation(br); if (!r.Contains(e.Location)) { return(false); } this.ToggleExpansion(olvItem.RowObject); return(true); }
/// <summary> /// Return the ListViewItem that appears immediately before the given item. /// If the given item is null, the last item in the list will be returned. /// Return null if the given item is the first item. /// </summary> /// <param name="itemToFind">The item that is before the item that is returned</param> /// <returns>A ListViewItem</returns> public override OLVListItem GetPreviousItem(OLVListItem itemToFind) { if (!this.ShowGroups) { return(base.GetPreviousItem(itemToFind)); } // Sanity if (this.OLVGroups == null || this.OLVGroups.Count == 0) { return(null); } // If the given items is null, return the last member of the last group if (itemToFind == null) { OLVGroup lastGroup = this.OLVGroups[this.OLVGroups.Count - 1]; return(this.GetItem(this.GroupingStrategy.GetGroupMember(lastGroup, lastGroup.VirtualItemCount - 1))); } // Find where this item occurs (which group and where in that group) int groupIndex = this.GroupingStrategy.GetGroup(itemToFind.Index); int indexWithinGroup = this.GroupingStrategy.GetIndexWithinGroup(this.OLVGroups[groupIndex], itemToFind.Index); // If it's not the first member of the group, just return the previous member if (indexWithinGroup > 0) { return(this.GetItem(this.GroupingStrategy.GetGroupMember(this.OLVGroups[groupIndex], indexWithinGroup - 1))); } // The item is the first member of its group. Return the last member of the previous group // (if there is one) if (groupIndex > 0) { OLVGroup previousGroup = this.OLVGroups[groupIndex - 1]; return(this.GetItem(this.GroupingStrategy.GetGroupMember(previousGroup, previousGroup.VirtualItemCount - 1))); } return(null); }
/// <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; } }
private void ApplyHyperlinkStyle(int rowIndex, OLVListItem olvi) { olvi.UseItemStyleForSubItems = false; // If subitem 0 is given a back color, the item back color is changed too. // So we have to remember it here so we can used it even if subitem 0 is changed. Color itemBackColor = olvi.BackColor; for (int i = 0; i < this.Columns.Count; i++) { OLVListSubItem subItem = olvi.GetSubItem(i); if (subItem == null) continue; OLVColumn column = this.GetColumn(i); subItem.BackColor = itemBackColor; if (column.Hyperlink && !String.IsNullOrEmpty(subItem.Url)) { if (this.IsUrlVisited(subItem.Url)) this.ApplyCellStyle(olvi, i, this.HyperlinkStyle.Visited); else this.ApplyCellStyle(olvi, i, this.HyperlinkStyle.Normal); } } }
/// <summary> /// Update the given row using the current hot item information /// </summary> /// <param name="olvi"></param> protected virtual void UpdateHotRow(OLVListItem olvi) { this.UpdateHotRow(this.HotRowIndex, this.HotColumnIndex, this.HotCellHitLocation, olvi); }
/// <summary> /// Update the ListViewItem with the data from its associated model. /// </summary> /// <remarks>This method does not resort or regroup the view. It simply updates /// the displayed data of the given item</remarks> public virtual void RefreshItem(OLVListItem olvi) { olvi.UseItemStyleForSubItems = true; olvi.SubItems.Clear(); this.FillInValues(olvi, olvi.RowObject); this.PostProcessOneRow(olvi.Index, this.GetItemIndexInDisplayOrder(olvi), olvi); }
/// <summary> /// Return the ListViewItem that appears immediately before the given item. /// If the given item is null, the last item in the list will be returned. /// Return null if the given item is the first item. /// </summary> /// <param name="itemToFind">The item that is before the item that is returned</param> /// <returns>A ListViewItem</returns> public virtual OLVListItem GetPreviousItem(OLVListItem itemToFind) { if (this.ShowGroups) { OLVListItem previousItem = null; foreach (ListViewGroup group in this.Groups) { foreach (OLVListItem lvi in group.Items) { if (lvi == itemToFind) return previousItem; else previousItem = lvi; } } if (itemToFind == null) return previousItem; else return null; } else { if (this.GetItemCount() == 0) return null; if (itemToFind == null) return this.GetItem(this.GetItemCount() - 1); if (itemToFind.Index == 0) return null; return this.GetItem(itemToFind.Index - 1); } }
/// <summary> /// Begin an edit operation on the given cell. /// </summary> /// <remarks>This performs various sanity checks and passes off the real work to StartCellEdit().</remarks> /// <param name="item">The row to be edited</param> /// <param name="subItemIndex">The index of the cell to be edited</param> public virtual void EditSubItem(OLVListItem item, int subItemIndex) { if (item == null) return; if (subItemIndex < 0 && subItemIndex >= item.SubItems.Count) return; if (this.CellEditActivation == CellEditActivateMode.None) return; if (!this.GetColumn(subItemIndex).IsEditable) return; this.StartCellEdit(item, subItemIndex); }
/// <summary> /// Apply a style to a cell /// </summary> /// <param name="olvi"></param> /// <param name="columnIndex"></param> /// <param name="style"></param> protected virtual void ApplyCellStyle(OLVListItem olvi, int columnIndex, IItemStyle style) { if (style == null) return; // Don't apply formatting to subitems when not in Details view if (this.View != View.Details && columnIndex > 0) return; olvi.UseItemStyleForSubItems = false; ListViewItem.ListViewSubItem subItem = olvi.SubItems[columnIndex]; if (style.Font != null) subItem.Font = style.Font; if (style.FontStyle != FontStyle.Regular) subItem.Font = new Font(subItem.Font ?? olvi.Font ?? this.Font, style.FontStyle); if (!style.ForeColor.IsEmpty) subItem.ForeColor = style.ForeColor; if (!style.BackColor.IsEmpty) subItem.BackColor = style.BackColor; }
/// <summary> /// Calculate the bounds of the edit control for the given item/column /// </summary> /// <param name="item"></param> /// <param name="subItemIndex"></param> /// <returns></returns> public Rectangle CalculateCellEditorBounds(OLVListItem item, int subItemIndex) { Rectangle r; if (this.View == View.Details) r = item.GetSubItemBounds(subItemIndex); else r = this.GetItemRect(item.Index, ItemBoundsPortion.Label); if (this.OwnerDraw) return CalculateCellEditorBoundsOwnerDrawn(item, subItemIndex, r); else return CalculateCellEditorBoundsStandard(item, subItemIndex, r); }
/// <summary> /// Return the bounds of the given cell only until the edge of the current text /// </summary> /// <param name="item">The row to be edited</param> /// <param name="subItemIndex">The index of the cell to be edited</param> /// <returns>A Rectangle</returns> public virtual Rectangle CalculateCellTextBounds(OLVListItem item, int subItemIndex) { return this.CalculateCellBounds(item, subItemIndex, ItemBoundsPortion.ItemOnly); }
/// <summary> /// Calculate the bounds of the edit control for the given item/column, when the listview /// is being owner drawn. /// </summary> /// <param name="item"></param> /// <param name="subItemIndex"></param> /// <param name="r"></param> /// <returns>A rectangle that is the bounds of the cell editor</returns> protected Rectangle CalculateCellEditorBoundsOwnerDrawn(OLVListItem item, int subItemIndex, Rectangle r) { IRenderer renderer = null; if (this.View == View.Details) renderer = this.GetColumn(subItemIndex).Renderer ?? this.DefaultRenderer; else renderer = this.ItemRenderer; if (renderer == null) return r; else { using (Graphics g = this.CreateGraphics()) { return renderer.GetEditRectangle(g, r, item, subItemIndex); } } }
/// <summary> /// Return the ListViewItem that appears immediately after the given item. /// If the given item is null, the first item in the list will be returned. /// Return null if the given item is the last item. /// </summary> /// <param name="itemToFind">The item that is before the item that is returned, or null</param> /// <returns>A ListViewItem</returns> public virtual OLVListItem GetNextItem(OLVListItem itemToFind) { if (this.ShowGroups) { bool isFound = (itemToFind == null); foreach (ListViewGroup group in this.Groups) { foreach (OLVListItem olvi in group.Items) { if (isFound) return olvi; isFound = (itemToFind == olvi); } } return null; } else { if (this.GetItemCount() == 0) return null; if (itemToFind == null) return this.GetItem(0); if (itemToFind.Index == this.GetItemCount() - 1) return null; return this.GetItem(itemToFind.Index + 1); } }
/// <summary> /// Fill in the given OLVListItem with values of the given row /// </summary> /// <param name="lvi">the OLVListItem that is to be stuff with values</param> /// <param name="rowObject">the model object from which values will be taken</param> protected virtual void FillInValues(OLVListItem lvi, object rowObject) { if (this.Columns.Count == 0) return; OLVListSubItem subItem = this.MakeSubItem(rowObject, this.GetColumn(0)); lvi.SubItems[0] = subItem; lvi.ImageSelector = subItem.ImageSelector; // Only Details and Tile views have subitems if (this.View == View.Details) { for (int i = 1; i < this.Columns.Count; i++) { lvi.SubItems.Add(this.MakeSubItem(rowObject, this.GetColumn(i))); } } else { if (this.View == View.Tile) { for (int i = 1; i < this.Columns.Count; i++) { OLVColumn column = this.GetColumn(i); if (column.IsTileViewColumn) lvi.SubItems.Add(this.MakeSubItem(rowObject, column)); } } } // Give the item the same font/colors as the control lvi.Font = this.Font; lvi.BackColor = this.BackColor; lvi.ForeColor = this.ForeColor; // Set the check state of the row, if we are showing check boxes if (this.CheckBoxes) { CheckState? state = this.GetCheckState(lvi.RowObject); if (state.HasValue) lvi.CheckState = (CheckState)state; } // Give the RowFormatter a chance to mess with the item if (this.RowFormatter != null) { this.RowFormatter(lvi); } }
/// <summary> /// Insert the given collection of objects before the given position /// </summary> /// <param name="index">Where to insert the objects</param> /// <param name="modelObjects">The objects to be inserted</param> /// <remarks> /// <para> /// This operation only makes sense of non-sorted, non-grouped /// lists, since any subsequent sort/group operation will rearrange /// the list. /// </para> /// <para>This method only works on ObjectListViews and FastObjectListViews.</para> ///</remarks> public virtual void InsertObjects(int index, ICollection modelObjects) { if (this.InvokeRequired) { this.Invoke((MethodInvoker)delegate() { this.InsertObjects(index, modelObjects); }); return; } if (modelObjects == null) return; this.BeginUpdate(); try { // Give the world a chance to cancel or change the added objects ItemsAddingEventArgs args = new ItemsAddingEventArgs(modelObjects); this.OnItemsAdding(args); if (args.Canceled) return; modelObjects = args.ObjectsToAdd; this.TakeOwnershipOfObjects(); ArrayList ourObjects = ObjectListView.EnumerableToArray(this.Objects, false); // If we are filtering the list, there is no way to efficiently // insert the objects, so just put them into our collection and rebuild. if (this.IsFiltering) { ourObjects.InsertRange(index, modelObjects); this.BuildList(true); return; } this.ListViewItemSorter = null; index = Math.Max(0, Math.Min(index, this.GetItemCount())); int i = index; foreach (object modelObject in modelObjects) { if (modelObject != null) { ourObjects.Insert(i, modelObject); OLVListItem lvi = new OLVListItem(modelObject); this.FillInValues(lvi, modelObject); this.Items.Insert(i, lvi); i++; } } for (i = index; i < this.GetItemCount(); i++) { OLVListItem lvi = this.GetItem(i); this.SetSubItemImages(lvi.Index, lvi); } // Tell the world that the list has changed this.OnItemsChanged(new ItemsChangedEventArgs()); } finally { this.EndUpdate(); } }
/// <summary> /// Do the work required after one item in a listview have been created /// </summary> protected virtual void PostProcessOneRow(int rowIndex, int displayIndex, OLVListItem olvi) { if (this.UseAlternatingBackColors && this.View == View.Details) { if (displayIndex % 2 == 1) { olvi.BackColor = this.AlternateRowBackColorOrDefault; } else { olvi.BackColor = this.BackColor; } } if (this.ShowImagesOnSubItems && !this.VirtualMode) { this.SetSubItemImages(rowIndex, olvi); } if (this.UseHyperlinks) { this.ApplyHyperlinkStyle(rowIndex, olvi); } this.TriggerFormatRowEvent(rowIndex, displayIndex, olvi); }
/// <summary> /// Really start an edit operation on a given cell. The parameters are assumed to be sane. /// </summary> /// <param name="item">The row to be edited</param> /// <param name="subItemIndex">The index of the cell to be edited</param> public virtual void StartCellEdit(OLVListItem item, int subItemIndex) { OLVColumn column = this.GetColumn(subItemIndex); Rectangle r = this.CalculateCellEditorBounds(item, subItemIndex); Control c = this.GetCellEditor(item, subItemIndex); c.Bounds = r; // Try to align the control as the column is aligned. Not all controls support this property Munger.PutProperty(c, "TextAlign", column.TextAlign); // Give the control the value from the model this.SetControlValue(c, column.GetValue(item.RowObject), column.GetStringValue(item.RowObject)); // Give the outside world the chance to munge with the process this.cellEditEventArgs = new CellEditEventArgs(column, c, r, item, subItemIndex); this.OnCellEditStarting(this.cellEditEventArgs); if (this.cellEditEventArgs.Cancel) return; // The event handler may have completely changed the control, so we need to remember it this.cellEditor = this.cellEditEventArgs.Control; // If the control isn't the height of the cell, centre it vertically. We don't // need to do this when in Tile view. if (this.View != View.Tile && this.cellEditor.Height != r.Height) this.cellEditor.Top += (r.Height - this.cellEditor.Height) / 2; this.Invalidate(); this.Controls.Add(this.cellEditor); this.ConfigureControl(); this.PauseAnimations(true); }
/// <summary> /// Tell the underlying list control which images to show against the subitems /// </summary> /// <param name="rowIndex">the index at which the item occurs</param> /// <param name="item">the item whose subitems are to be set</param> /// <param name="shouldClearImages">will existing images be cleared if no new image is provided?</param> protected virtual void SetSubItemImages(int rowIndex, OLVListItem item, bool shouldClearImages) { if (!this.ShowImagesOnSubItems || this.OwnerDraw) return; for (int i = 1; i < item.SubItems.Count; i++) { this.SetSubItemImage(rowIndex, i, item.GetSubItem(i), shouldClearImages); } }
/// <summary> /// Apply a style to the given row /// </summary> /// <param name="olvi"></param> /// <param name="style"></param> protected virtual void ApplyRowStyle(OLVListItem olvi, IItemStyle style) { if (style == null) return; if (this.FullRowSelect || this.View != View.Details) { if (style.Font != null) olvi.Font = style.Font; if (style.FontStyle != FontStyle.Regular) olvi.Font = new Font(olvi.Font ?? this.Font, style.FontStyle); if (!style.ForeColor.IsEmpty) { if (olvi.UseItemStyleForSubItems) olvi.ForeColor = style.ForeColor; else { foreach (ListViewItem.ListViewSubItem x in olvi.SubItems) { x.ForeColor = style.ForeColor; } } } if (!style.BackColor.IsEmpty) { if (olvi.UseItemStyleForSubItems) olvi.BackColor = style.BackColor; else { foreach (ListViewItem.ListViewSubItem x in olvi.SubItems) { x.BackColor = style.BackColor; } } } } else { olvi.UseItemStyleForSubItems = false; foreach (ListViewItem.ListViewSubItem x in olvi.SubItems) { if (style.BackColor.IsEmpty) x.BackColor = olvi.BackColor; else x.BackColor = style.BackColor; } this.ApplyCellStyle(olvi, 0, style); } }
/// <summary> /// Given the item and the subitem, calculate its bounds. /// </summary> /// <param name="item"></param> /// <param name="subItem"></param> /// <returns></returns> public Rectangle CalculateItemBounds(OLVListItem item, OLVListSubItem subItem) { if (item == null) return Rectangle.Empty; if (subItem == null) return item.Bounds; else return item.GetSubItemBounds(item.SubItems.IndexOf(subItem)); }
/// <summary> /// Calculate the bounds of the edit control for the given item/column, when the listview /// is not being owner drawn. /// </summary> /// <param name="item"></param> /// <param name="subItemIndex"></param> /// <param name="cellBounds"></param> /// <returns>A rectangle that is the bounds of the cell editor</returns> protected Rectangle CalculateCellEditorBoundsStandard(OLVListItem item, int subItemIndex, Rectangle cellBounds) { if (this.View != View.Details) return cellBounds;// // Allow for image (if there is one) int offset = 0; object subItemImageSelector = item.ImageSelector; if (subItemIndex > 0) subItemImageSelector = ((OLVListSubItem)item.SubItems[subItemIndex]).ImageSelector; if (this.GetActualImageIndex(subItemImageSelector) != -1) { offset += this.SmallImageSize.Width + 2; } // Allow for checkbox if (this.CheckBoxes && this.StateImageList != null && subItemIndex == 0) { offset += this.StateImageList.ImageSize.Width + 2; } // Allow for indent (first column only) if (subItemIndex == 0 && item.IndentCount > 0) { offset += (this.SmallImageSize.Width * item.IndentCount); } // Do the adjustment if (offset > 0) { cellBounds.X += offset; cellBounds.Width -= offset; } return cellBounds; }
/// <summary> /// Create a new event block /// </summary> /// <param name="column"></param> /// <param name="item"></param> /// <param name="subItemIndex"></param> /// <param name="currentValue"></param> /// <param name="newValue"></param> public SubItemCheckingEventArgs(OLVColumn column, OLVListItem item, int subItemIndex, CheckState currentValue, CheckState newValue) { this.column = column; this.listViewItem = item; this.subItemIndex = subItemIndex; this.currentValue = currentValue; this.newValue = newValue; }
/// <summary> /// Return a control that can be used to edit the value of the given cell. /// </summary> /// <param name="item">The row to be edited</param> /// <param name="subItemIndex">The index of the cell to be edited</param> /// <returns>A Control to edit the given cell</returns> protected virtual Control GetCellEditor(OLVListItem item, int subItemIndex) { OLVColumn column = this.GetColumn(subItemIndex); Object value = column.GetValue(item.RowObject) ?? this.GetFirstNonNullValue(column); // TODO: What do we do if value is still null here? // Ask the registry for an instance of the appropriate editor. Control editor = ObjectListView.EditorRegistry.GetEditor(item.RowObject, column, value); // Use a default editor if the registry can't create one for us. if (editor == null) editor = this.MakeDefaultCellEditor(column); return editor; }
/// <summary> /// Create a new event args /// </summary> /// <param name="model"></param> /// <param name="item"></param> public TreeBranchExpandingEventArgs(object model, OLVListItem item) { this.Model = model; this.Item = item; }
/// <summary> /// Tell the underlying list control which images to show against the subitems /// </summary> /// <param name="rowIndex">the index at which the item occurs</param> /// <param name="item">the item whose subitems are to be set</param> protected virtual void SetSubItemImages(int rowIndex, OLVListItem item) { this.SetSubItemImages(rowIndex, item, false); }
/// <summary> /// Create a new event args /// </summary> /// <param name="model"></param> /// <param name="item"></param> public TreeBranchCollapsedEventArgs(object model, OLVListItem item) { this.Model = model; this.Item = item; }
/// <summary> /// Trigger FormatRow and possibly FormatCell events for the given item /// </summary> /// <param name="rowIndex"></param> /// <param name="displayIndex"></param> /// <param name="olvi"></param> protected virtual void TriggerFormatRowEvent(int rowIndex, int displayIndex, OLVListItem olvi) { FormatRowEventArgs args = new FormatRowEventArgs(); args.ListView = this; args.RowIndex = rowIndex; args.DisplayIndex = displayIndex; args.Item = olvi; args.UseCellFormatEvents = this.UseCellFormatEvents; this.OnFormatRow(args); if (args.UseCellFormatEvents && this.View == View.Details) { // If a cell isn't given its own color, it should use the color of the item. // However, there is a bug in the .NET framework where the cell are given // the color of the ListView instead. So we have to explicitly give each // cell the back color that it should have. olvi.UseItemStyleForSubItems = false; Color backColor = olvi.BackColor; for (int i = 0; i < this.Columns.Count; i++) { olvi.SubItems[i].BackColor = backColor; } // Fire one event per cell FormatCellEventArgs args2 = new FormatCellEventArgs(); args2.ListView = this; args2.RowIndex = rowIndex; args2.DisplayIndex = displayIndex; args2.Item = olvi; for (int i = 0; i < this.Columns.Count; i++) { args2.ColumnIndex = i; args2.Column = this.GetColumn(i); args2.SubItem = olvi.GetSubItem(i); this.OnFormatCell(args2); } } }
/// <summary> /// Update the given row using the given hot item information /// </summary> /// <param name="rowIndex"></param> /// <param name="columnIndex"></param> /// <param name="hitLocation"></param> /// <param name="olvi"></param> protected virtual void UpdateHotRow(int rowIndex, int columnIndex, HitTestLocation hitLocation, OLVListItem olvi) { if (rowIndex < 0 || columnIndex < 0) return; if (this.UseHyperlinks) { OLVColumn column = this.GetColumn(columnIndex); OLVListSubItem subItem = olvi.GetSubItem(columnIndex); if (column.Hyperlink && hitLocation == HitTestLocation.Text && !String.IsNullOrEmpty(subItem.Url)) { this.ApplyCellStyle(olvi, columnIndex, this.HyperlinkStyle.Over); this.Cursor = this.HyperlinkStyle.OverCursor ?? Cursors.Default; } else { this.Cursor = Cursors.Default; } } if (this.UseHotItem) { if (!olvi.Selected) { this.ApplyRowStyle(olvi, this.HotItemStyle); } } }
/// <summary> /// Build/rebuild all the list view items in the list /// </summary> /// <param name="shouldPreserveState">If this is true, the control will try to preserve the selection, /// focused item, and the scroll position (see Remarks) /// </param> /// <remarks> /// <para> /// Use this method in situations were the contents of the list is basically the same /// as previously. /// </para> /// <para> /// Due to limitations in .NET's ListView, the scroll position is only preserved if /// the control is in Details view AND it is not showing groups. /// </para> /// </remarks> public virtual void BuildList(bool shouldPreserveState) { if (this.Frozen) return; this.ApplyExtendedStyles(); this.ClearHotItem(); int previousTopIndex = this.TopItemIndex; Point currentScrollPosition = this.LowLevelScrollPosition; IList previousSelection = new ArrayList(); Object previousFocus = null; if (shouldPreserveState && this.objects != null) { previousSelection = this.SelectedObjects; OLVListItem focusedItem = this.FocusedItem as OLVListItem; if (focusedItem != null) previousFocus = focusedItem.RowObject; } IEnumerable objectsToDisplay = this.FilteredObjects; this.BeginUpdate(); try { this.Items.Clear(); this.ListViewItemSorter = null; if (objectsToDisplay != null) { // Build a list of all our items and then display them. (Building // a list and then doing one AddRange is about 10-15% faster than individual adds) List<OLVListItem> itemList = new List<OLVListItem>(); foreach (object rowObject in objectsToDisplay) { OLVListItem lvi = new OLVListItem(rowObject); this.FillInValues(lvi, rowObject); itemList.Add(lvi); } this.Items.AddRange(itemList.ToArray()); this.Sort(); if (shouldPreserveState) { this.SelectedObjects = previousSelection; this.FocusedItem = this.ModelToItem(previousFocus); } this.RefreshHotItem(); } } finally { this.EndUpdate(); } // We can only restore the scroll position after the EndUpdate() because // of caching that the ListView does internally during a BeginUpdate/EndUpdate pair. if (shouldPreserveState) { this.RefreshHotItem(); // Restore the scroll position. TopItemIndex is best, but doesn't work // when the control is grouped. if (this.ShowGroups) this.LowLevelScroll(currentScrollPosition.X, currentScrollPosition.Y); else this.TopItemIndex = previousTopIndex; } }
private Rectangle CalculateCellBounds(OLVListItem item, int subItemIndex, ItemBoundsPortion portion) { // SubItem.Bounds works for every subitem, except the first. if (subItemIndex > 0) return item.SubItems[subItemIndex].Bounds; // For non detail views, we just use the requested portion Rectangle r = this.GetItemRect(item.Index, portion); if (r.Y < -10000000 || r.Y > 10000000) { r.Y = item.Bounds.Y; } if (this.View != View.Details) return r; // Finding the bounds of cell 0 should not be a difficult task, but it is. Problems: // 1) item.SubItem[0].Bounds is always the full bounds of the entire row, not just cell 0. // 2) if column 0 has been dragged to some other position, the bounds always has a left edge of 0. // We avoid both these problems by using the position of sides the column header to calculate // the sides of the cell Point sides = NativeMethods.GetScrolledColumnSides(this, 0); r.X = sides.X + 4; r.Width = sides.Y - sides.X - 5; return r; }
/// <summary> /// Return the bounds of the given cell /// </summary> /// <param name="item">The row to be edited</param> /// <param name="subItemIndex">The index of the cell to be edited</param> /// <returns>A Rectangle</returns> public virtual Rectangle CalculateCellBounds(OLVListItem item, int subItemIndex) { // We use ItemBoundsPortion.Label rather than ItemBoundsPortion.Item // since Label extends to the right edge of the cell, whereas Item gives just the // current text width. return this.CalculateCellBounds(item, subItemIndex, ItemBoundsPortion.Label); }