« Drag and Drop | Index | Empty Text » |
Embedded editing controls can be thought of as an extension to label edit functionality (see Label Editing for more information).
Every item and sub-item has a cell area on which an editing control can be placed. In the simplest case (LabelEdit set to true), the editing control is basically a System.Windows.Forms.TextBox control. It is actually an instance of BetterListViewTextBoxEmbeddedControl, which is a TextBox wrapper implementing IBetterListViewEmbeddedControl interface. Any control can be used as embedded control in Bettter ListView if implements one of these interfaces:
IBetterListViewEmbeddedControl
IBetterListViewEmbeddedControlExtended
The custom embedded control is shown on the image below. When user clicks on sub-item text (an abbreviation of tea grading), an editing control appears on the top-left corner of the cell area. The control contains buttons for accepting and cancelling changes:
This interface contains prescription for minimum amount of functionality required by an embedded control:
Get label text for currently edited data.
Data accepting and cancelling events (e.g. if the control has an OK button).
Set control size given the cell area and positioning data.
Move data from sub-item to the control.
Move data from the control to the sub-item.
Let's make a sample control. We will make a TextBox-based embedded control for editing words in lower-case. First, we inherit TextBox and implement IBetterListViewEmbeddedControl interface:
C#
/// <summary>
/// Represents a custom control embeddable in Better ListView.
/// </summary>
public class TextBoxEmbeddedControl : TextBox, IBetterListViewEmbeddedControl
Visual Basic
''' <summary>
''' Represents a custom control embeddable in Better ListView.
''' </summary>
Public Class TextBoxEmbeddedControl
Inherits TextBox
Implements IBetterListViewEmbeddedControl
Then we implement the LabelText property:
C#
/// <summary>
/// current (edited) label text
/// </summary>
public string LabelText
{
get
{
return Text.ToLower();
}
}
Visual Basic
''' <summary>
''' current (edited) label text
''' </summary>
Public ReadOnly Property LabelText() As String
Get
Return Text.ToLower()
End Get
End Property
As you can see, the text of the TextBox is converted to lower case since we want item/sub-item labels to be only in lower case.
Next, we implement RequestAccept and RequestCancel events:
C#
/// <summary>
/// request accepting updated data in BetterListView
/// </summary>
public event EventHandler RequestAccept;
/// <summary>
/// request cancelling editing
/// </summary>
public event EventHandler RequestCancel;
Visual Basic
''' <summary>
''' request accepting updated data in BetterListView
''' </summary>
Public Event RequestAccept As EventHandler Implements IBetterListViewEmbeddedControl.RequestAccept
''' <summary>
''' request cancelling editing
''' </summary>
Public Event RequestCancel As EventHandler Implements IBetterListViewEmbeddedControl.RequestCancel
Next, we implement GetData and SetData methods:
C#
/// <summary>
/// get data from the specified sub-item in control
/// </summary>
/// <param name = "subItem">sub-item whose data are being edited</param>
public void GetData(BetterListViewSubItem subItem)
{
Text = subItem.Text;
}
/// <summary>
/// set data from control to the specified sub-item
/// </summary>
/// <param name = "subItem">sub-item whose data are being edited</param>
public void SetData(BetterListViewSubItem subItem)
{
subItem.Text = LabelText;
}
Visual Basic
''' <summary>
''' get data from the specified sub-item in control
''' </summary>
''' <param name = "subItem">sub-item whose data are being edited</param>
Public Sub GetData(ByVal subItem As BetterListViewSubItem) Implements IBetterListViewEmbeddedControl.GetData
Text = subItem.Text
End Sub
''' <summary>
''' set data from control to the specified sub-item
''' </summary>
''' <param name = "subItem">sub-item whose data are being edited</param>
Public Sub SetData(ByVal subItem As BetterListViewSubItem) Implements IBetterListViewEmbeddedControl.SetData
subItem.Text = LabelText
End Sub
These method are trivial since we need not to do any data conversions (the only conversion here is lowering the case of edited text in the LabelText getter).
The last method contained in the interface is SetSize method, which needs not to be implemented (the body can be kept empty). You implement this method only if you need to adjust control's size when label edit starts.
The constructor should be implemented like this:
C#
/// <summary>
/// Initializes a new instance of the <see cref = "TextBoxEmbeddedControl" /> class.
/// </summary>
public TextBoxEmbeddedControl()
{
AcceptsReturn = true;
CausesValidation = false;
}
Visual Basic
''' <summary>
''' Initializes a new instance of the <see cref = "TextBoxEmbeddedControl" /> class.
''' </summary>
Public Sub New()
AcceptsReturn = True
CausesValidation = False
End Sub
The AcceptsReturn property is set to true because we will handle the ENTER key (and raise RequestAccept event appropriately).
The CausesValidation property is set to false because it is a good practice in this situation.
Both input and output data are validated in the IBetterListViewEmbeddedControl implementation and validation of some third-party controls can prevent whole form with the control from closing.
The last thing we implement is handling of the ENTER key for accepting the data and the ESCAPE key for cancelling:
C#
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter &&
RequestAccept != null)
{
RequestAccept(this, EventArgs.Empty);
e.Handled = true;
return;
}
if (e.KeyCode == Keys.Escape &&
RequestCancel != null)
{
RequestCancel(this, EventArgs.Empty);
e.Handled = true;
return;
}
base.OnKeyDown(e);
}
Visual Basic
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
If e.KeyCode = Keys.Enter AndAlso RequestAccept IsNot Nothing Then
RequestAccept(Me, EventArgs.Empty)
e.Handled = True
Return
End If
If e.KeyCode = Keys.Escape AndAlso RequestCancel IsNot Nothing Then
RequestCancel(Me, EventArgs.Empty)
e.Handled = True
Return
End If
MyBase.OnKeyDown(e)
End Sub
It is a common good practice to implement interfaces explicitly. The sample implementation is implicit for the sake of better readability. Embedded controls implemented in BetterListView.dll are implemented implicitly (and marked virtual) to allow for being inherited (e.g. MyCustomControl : BetterListViewEmbeddedControl) and you may possibly want to override any part of the interface implementation.
The extended interface has currently only one method called RequestEndEdit. This method can be called by the Better ListView, when it asks the control whether it is ready to end editing. The control can return a boolean value (true - continue EndEdit, false - refuse to end editing). There are many situations when the label editing is terminated (e.g. scrolling the control, selecting items...) and terminating the label edit is not always wanted (this is a case of System.Windows.Forms.DateTimePicker control, which sometimes behaves as being transparent for mouse clicks and thus being closed because of click-through on the Better ListView client area - the RequestEndEdit method fixes such possible behavior of third party controls).
Form with Better ListView containing some columns and items:
C#
/// <summary>
/// Shows embedding of custom controls into Better ListView.
/// </summary>
internal sealed partial class EmbeddedControlSampleForm : Form
{
/// <summary>
/// Initializes a new instance of the <see cref = "EmbeddedControlSampleForm" /> class.
/// </summary>
public EmbeddedControlSampleForm()
{
InitializeComponent();
this.listView.BeginUpdate();
this.listView.Columns.AddRange(new[]
{
new BetterListViewColumnHeader("Document name", 160),
new BetterListViewColumnHeader("Access", 128)
});
this.listView.Items.AddRange(
new[]
{
new BetterListViewItem(new[] { "hydro-report.pdf", "read" }),
new BetterListViewItem(new[] { "magnetic_resonance.docx", "read write" }),
new BetterListViewItem(new[] { "billing forms (2011).zip", "read" })
});
this.listView.LabelEditActivation = (BetterListViewLabelEditActivation.Keyboard | BetterListViewLabelEditActivation.SingleClick);
this.listView.LabelEditModeSubItems = BetterListViewLabelEditMode.CustomControl;
this.listView.EndUpdate();
this.listView.RequestEmbeddedControl += ListViewRequestEmbeddedControl;
}
private IBetterListViewEmbeddedControl ListViewRequestEmbeddedControl(object sender, BetterListViewRequestEmbeddedControlEventArgs eventArgs)
{
if (eventArgs.SubItem.Index == 1)
{
return (new DocumentAccessConrol());
}
return null;
}
}
Visual Basic
''' <summary>
''' Shows embedding of custom controls into Better ListView.
''' </summary>
Partial Friend NotInheritable Class EmbeddedControlSampleForm
''' <summary>
''' Initializes a new instance of the <see cref = "EmbeddedControlSampleForm" /> class.
''' </summary>
Public Sub New()
InitializeComponent()
ListView.BeginUpdate()
ListView.Columns.AddRange(
New BetterListViewColumnHeader() { _
New BetterListViewColumnHeader("Document name", 160),
New BetterListViewColumnHeader("Access", 128)
})
ListView.Items.AddRange(
New BetterListViewItem() { _
New BetterListViewItem(New String() {"hydro-report.pdf", "read"}),
New BetterListViewItem(New String() {"magnetic_resonance.docx", "read write"}),
New BetterListViewItem(New String() {"billing forms (2011).zip", "read"})
})
ListView.LabelEditActivation =
(BetterListViewLabelEditActivation.Keyboard Or BetterListViewLabelEditActivation.SingleClick)
ListView.LabelEditModeSubItems = BetterListViewLabelEditMode.CustomControl
ListView.EndUpdate()
AddHandler ListView.RequestEmbeddedControl, AddressOf ListViewRequestEmbeddedControl
End Sub
Private Function ListViewRequestEmbeddedControl(ByVal sender As Object,
ByVal eventArgs As BetterListViewRequestEmbeddedControlEventArgs) _
As IBetterListViewEmbeddedControl
If eventArgs.SubItem.Index = 1 Then
Return (New DocumentAccessConrol())
End If
Return Nothing
End Function
End Class
DocumentAccessControl class used as complex embedded control (see EmbeddedControlSampleForm sample in the provided C# and Visual Basic samples for full source code):
C#
/// <summary>
/// Represents a custom control embeddable in Better ListView.
/// </summary>
[ToolboxItem(false)]
internal sealed partial class DocumentAccessConrol : UserControl, IBetterListViewEmbeddedControl
{
private const string StringRead = "read";
private const string StringWrite = "write";
/// <summary>
/// current (edited) label text
/// </summary>
public string LabelText
{
get
{
// convert control's state to label
if (this.checkBoxRead.Checked &&
this.checkBoxWrite.Checked)
{
return String.Format("{0} {1}", StringRead, StringWrite);
}
if (this.checkBoxRead.Checked)
{
return StringRead;
}
if (this.checkBoxWrite.Checked)
{
return StringWrite;
}
return String.Empty;
}
}
/// <summary>
/// request accepting updated data in BetterListView
/// </summary>
public event EventHandler RequestAccept;
/// <summary>
/// request cancelling editing
/// </summary>
public event EventHandler RequestCancel;
/// <summary>
/// Initializes a new instance of the <see cref = "DocumentAccessConrol" /> class.
/// </summary>
public DocumentAccessConrol()
{
InitializeComponent();
//NOTE: disabling validation prevents form close cancellation
CausesValidation = false;
foreach (Control control in Controls)
{
control.LostFocus += ControlOnLostFocus;
}
}
/// <summary>
/// get data from the specified sub-item in control
/// </summary>
/// <param name = "subItem">sub-item whose data are being edited</param>
public void GetData(BetterListViewSubItem subItem)
{
// convert label to control's state
this.checkBoxRead.Checked = subItem.Text.Contains(StringRead);
this.checkBoxWrite.Checked = subItem.Text.Contains(StringWrite);
}
/// <summary>
/// set data from control to the specified sub-item
/// </summary>
/// <param name = "subItem">sub-item whose data are being edited</param>
public void SetData(BetterListViewSubItem subItem)
{
subItem.Text = LabelText;
}
/// <summary>
/// set control size
/// </summary>
/// <param name = "subItem">sub-item whose data are being edited</param>
/// <param name = "placement">placement of the embedded control within sub-item</param>
public void SetSize(BetterListViewSubItem subItem, BetterListViewEmbeddedControlPlacement placement)
{
// keep size of the control unchanged
}
private void ControlOnLostFocus(object sender, EventArgs eventArgs)
{
//
// NOTE: this code is needed just for hiding embedded control with sub-controls when user changes active form while label editing
//
bool anyFocused = Focused;
if (anyFocused == false)
{
foreach (Control control in Controls)
{
if (control.Focused)
{
anyFocused = true;
break;
}
}
}
if (anyFocused == false)
{
RequestAccept(this, eventArgs);
}
}
private void ButtonOKClick(object sender, EventArgs e)
{
RequestAccept(this, e);
}
private void ButtonCancelClick(object sender, EventArgs e)
{
RequestCancel(this, e);
}
}
Visual Basic
''' <summary>
''' Represents a custom control embeddable in Better ListView.
''' </summary>
<ToolboxItem(False)>
Partial Friend NotInheritable Class DocumentAccessConrol
Inherits UserControl
Implements IBetterListViewEmbeddedControl
Private Const StringRead As String = "read"
Private Const StringWrite As String = "write"
''' <summary>
''' current (edited) label text
''' </summary>
Public ReadOnly Property LabelText() As String Implements IBetterListViewEmbeddedControl.LabelText
Get
' convert control's state to label
If CheckBoxRead.Checked AndAlso CheckBoxWrite.Checked Then
Return [String].Format("{0} {1}", StringRead, StringWrite)
End If
If CheckBoxRead.Checked Then
Return StringRead
End If
If CheckBoxWrite.Checked Then
Return StringWrite
End If
Return [String].Empty
End Get
End Property
''' <summary>
''' request accepting updated data in BetterListView
''' </summary>
Public Event RequestAccept As EventHandler Implements IBetterListViewEmbeddedControl.RequestAccept
''' <summary>
''' request cancelling editing
''' </summary>
Public Event RequestCancel As EventHandler Implements IBetterListViewEmbeddedControl.RequestCancel
''' <summary>
''' Initializes a new instance of the <see cref = "DocumentAccessConrol" /> class.
''' </summary>
Public Sub New()
InitializeComponent()
'NOTE: disabling validation prevents form close cancellation
CausesValidation = False
For Each control As Control In Controls
AddHandler control.LostFocus, AddressOf ControlOnLostFocus
Next
End Sub
''' <summary>
''' get data from the specified sub-item in control
''' </summary>
''' <param name = "subItem">sub-item whose data are being edited</param>
Public Sub GetData(ByVal subItem As BetterListViewSubItem) Implements IBetterListViewEmbeddedControl.GetData
' convert label to control's state
CheckBoxRead.Checked = subItem.Text.Contains(StringRead)
CheckBoxWrite.Checked = subItem.Text.Contains(StringWrite)
End Sub
''' <summary>
''' set data from control to the specified sub-item
''' </summary>
''' <param name = "subItem">sub-item whose data are being edited</param>
Public Sub SetData(ByVal subItem As BetterListViewSubItem) Implements IBetterListViewEmbeddedControl.SetData
subItem.Text = LabelText
End Sub
''' <summary>
''' set control size
''' </summary>
''' <param name = "subItem">sub-item whose data are being edited</param>
''' <param name = "placement">placement of the embedded control within sub-item</param>
Public Sub SetSize(ByVal subItem As BetterListViewSubItem,
ByVal placement As BetterListViewEmbeddedControlPlacement) _
Implements IBetterListViewEmbeddedControl.SetSize
' keep size of the control unchanged
End Sub
Private Sub ControlOnLostFocus(ByVal sender As Object, ByVal eventArgs As EventArgs)
'
' NOTE: this code is needed just for hiding embedded control with sub-controls when user changes active form while label editing
'
Dim anyFocused As Boolean = Focused
If anyFocused = False Then
For Each control As Control In Controls
If control.Focused Then
anyFocused = True
Exit For
End If
Next
End If
If anyFocused = False Then
RaiseEvent RequestAccept(Me, eventArgs)
End If
End Sub
Private Sub ButtonOKClick(ByVal sender As Object, ByVal e As EventArgs) Handles ButtonOK.Click
RaiseEvent RequestAccept(Me, e)
End Sub
Private Sub ButtonCancelClick(ByVal sender As Object, ByVal e As EventArgs) Handles ButtonCancel.Click
RaiseEvent RequestCancel(Me, e)
End Sub
End Class
« Drag and Drop | Index | Empty Text » |
Better ListView Documentation | Copyright © 2010-2012 ComponentOwl.com |