Guys, I need your help again. Strange, but the issue returned back after I have modified few thing that has no relation to item height from the first sight at list.
I have two issues, both are observable when you have more list items than can be visible in listview, in other words you have to have many items and vertical scroll bar.
1) Just add items to list view so that the scroll bar starts to be visible, and you can see, that the last item is only partially visible. If you try to drag the scroll bar down, you will notice, that it is already in the lowest position, and you still can't see the lowest item.
2) This makes me worry even more, try to create many items, so that their summary height will be at list 3 times the height of the list view.
Now drag the scroll bar up and down many times. First of all I notice that the scroll bar itself changes it's size. And what is even worse - after 2-3 drags you will notice that the lowest items are not visible at all now, and the more times you will drag the scroll bar more items will go below visible lower bound of the list view.
The code below is the code of my user control, that contains the list view:
public
partial
class
TracerOutput : UserControl
{
public
TracerOutput()
{
InitializeComponent();
_messages =
new
List<ListViewDataItem>();
_visibleMessages =
new
List<ListViewDataItem>();
InitializeListView();
_isErrorsVisible = _isRegularsVisible = _isWarningsVisible =
true
;
}
#region Members
private
string
_defaultSaveLogPath =
"C:\\"
;
private
Color _warningColor = Color.Blue;
private
Color _errorColor = Color.Red;
private
Color _informationColor = Color.Black;
private
Color _timeStampColor = Color.Green;
private
Font _warningFont =
new
Font(
"Tahoma"
, 9, FontStyle.Bold, GraphicsUnit.Point);
private
Font _errorFont =
new
Font(
"Tahoma"
, 9, FontStyle.Bold, GraphicsUnit.Point);
private
Font _informationFont =
new
Font(
"Tahoma"
, 9, FontStyle.Bold, GraphicsUnit.Point);
private
Font _timeStampFont =
new
Font(
"Tahoma"
, 9, FontStyle.Bold, GraphicsUnit.Point);
public
Action ErrorMessageDisplayed;
private
Color _backgroundColor = Color.White;
private
bool
_isErrorsVisible, _isWarningsVisible, _isRegularsVisible;
private
RadListView _textArea;
private
List<ListViewDataItem> _messages;
// All messages that tracer have from logger
private
List<ListViewDataItem> _visibleMessages;
// Messages that should be displayed according to user's filtering
private
bool
_scrollToLast =
true
;
#endregion
#region Properties
private
List<TraceMessage.MessageTypes> VisibleMessageTypes
{
get
{
var visibleTypes =
new
List<TraceMessage.MessageTypes>();
if
(_isErrorsVisible)
visibleTypes.Add(TraceMessage.MessageTypes.Error);
if
(_isWarningsVisible)
visibleTypes.Add(TraceMessage.MessageTypes.Warning);
if
(_isRegularsVisible)
visibleTypes.Add(TraceMessage.MessageTypes.Information);
return
visibleTypes;
}
}
/// <summary>
/// Get/Set the default folder path for saving log to textual file
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Default folder for saving log files"
),
DefaultValue(
"C:\\"
)]
public
string
SaveLogDefaultPath
{
get
{
return
_defaultSaveLogPath; }
set
{
_defaultSaveLogPath = value;
Invalidate();
}
}
/// <summary>
/// Get/Set tracer background color
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Tracer background color"
),
DefaultValue(
typeof
(Color),
"White"
)]
public
Color BackgroundColor
{
get
{
return
_backgroundColor; }
set
{
_backgroundColor = value;
Invalidate();
}
}
/// <summary>
/// Get/Set warning message text color
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Warning messages text color"
),
DefaultValue(
typeof
(Color),
"Blue"
)]
public
Color WarningColor
{
get
{
return
_warningColor; }
set
{
_warningColor = value;
Invalidate();
}
}
/// <summary>
/// Get/Set error message text color
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Error messages text color"
),
DefaultValue(
typeof
(Color),
"Red"
)]
public
Color ErrorColor
{
get
{
return
_errorColor; }
set
{
_errorColor = value;
Invalidate();
}
}
/// <summary>
/// Get/Set information message text color
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Information messages text color"
),
DefaultValue(
typeof
(Color),
"Black"
)]
public
Color InfoColor
{
get
{
return
_informationColor; }
set
{
_informationColor = value;
Invalidate();
}
}
/// <summary>
/// Get/Set time stamp font color
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Time stamp font color"
),
DefaultValue(
typeof
(Color),
"Green"
)]
public
Color TimeStampColor
{
get
{
return
_timeStampColor; }
set
{
_timeStampColor = value;
Invalidate();
}
}
/// <summary>
/// Get/Set warning message text font
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Warning messages text font"
),
DefaultValue(
typeof
(Font),
"Tahoma, 9pt,style=Bold"
)]
public
Font WarningFont
{
get
{
return
_warningFont; }
set
{
_warningFont = value;
Invalidate();
}
}
/// <summary>
/// Get/Set error message text font
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Error messages text font"
),
DefaultValue(
typeof
(Font),
"Tahoma, 9pt,style=Bold"
)]
public
Font ErrorFont
{
get
{
return
_errorFont; }
set
{
_errorFont = value;
Invalidate();
}
}
/// <summary>
/// Get/Set information message text font
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Information messages text font"
),
DefaultValue(
typeof
(Font),
"Tahoma, 9pt,style=Bold"
)]
public
Font InfoFont
{
get
{
return
_informationFont; }
set
{
_informationFont = value;
Invalidate();
}
}
/// <summary>
/// Get/Set time stamp font
/// </summary>
[Browsable(
true
),
Category(
"SpecialProperties"
),
Description(
"Time stamp font"
),
DefaultValue(
typeof
(Font),
"Tahoma, 9pt,style=Bold"
)]
public
Font TimeStampFont
{
get
{
return
_timeStampFont; }
set
{
_timeStampFont = value;
Invalidate();
}
}
#endregion
#region Methods
private
void
InitializeListView()
{
_textArea =
new
RadListView
{
AllowEdit =
false
,
AllowRemove =
false
,
AutoScroll =
true
,
AutoSize =
false
,
BackColor = BackgroundColor,
ShowCheckBoxes =
false
,
ShowGroups =
false
,
ShowItemToolTips =
false
,
Dock = DockStyle.Fill,
ViewType = ListViewType.ListView,
AllowArbitraryItemHeight =
true
,
AllowArbitraryItemWidth =
false
,
HotTracking =
true
,
ItemSpacing = 0,
HorizontalScrollState = ScrollState.AlwaysHide,
VerticalScrollState = ScrollState.AutoHide
};
_textArea.VisualItemFormatting += OnTextAreaVisualItemFormatting;
_textArea.VisualItemCreating += OnTextAreaVisualItemCreating;
_textArea.SizeChanged += OnTextAreaSizeChanged;
pnlPlaceholder.Controls.Add(_textArea);
}
private
List<ListViewDataItem> GetVisibleItems()
{
return
_messages.FindAll(m => (((TraceMessage)m.Tag).Type == TraceMessage.MessageTypes.Warning && _isWarningsVisible) ||
(((TraceMessage)m.Tag).Type == TraceMessage.MessageTypes.Error && _isErrorsVisible) ||
(((TraceMessage)m.Tag).Type == TraceMessage.MessageTypes.Information && _isRegularsVisible));
}
private
void
RedrawMessages()
{
_textArea.Items.Clear();
_visibleMessages = GetVisibleItems();
_textArea.BeginUpdate();
_textArea.Items.AddRange(_visibleMessages.ToArray());
_textArea.EndUpdate();
}
public
void
SaveLogToFile(
bool
isOnlyVisible)
{
const
string
logFilter =
"Log files (*.txt)|*.txt"
;
const
string
logFileExt =
"txt"
;
const
string
dialogTitle =
"Export program log to file ..."
;
var saveLogDialog = GetSaveFileDialog(dialogTitle, logFilter, logFileExt);
if
(DialogResult.OK == saveLogDialog.ShowDialog())
{
var visibleTypes = isOnlyVisible
? VisibleMessageTypes
: Enum.GetValues(
typeof
(TraceMessage.MessageTypes)).Cast<TraceMessage.MessageTypes>().ToList();
if
(SaveToFileRequested !=
null
)
SaveToFileRequested(saveLogDialog.FileName, visibleTypes);
}
}
private
SaveFileDialog GetSaveFileDialog(
string
title,
string
filter,
string
defaultExtension)
{
var saveFileDialog =
new
SaveFileDialog
{
InitialDirectory = SaveLogDefaultPath,
Title = title,
Filter = filter,
CreatePrompt =
false
,
CheckFileExists =
false
,
CheckPathExists =
false
,
AutoUpgradeEnabled =
true
,
SupportMultiDottedExtensions =
false
,
OverwritePrompt =
true
,
DefaultExt = defaultExtension,
AddExtension =
true
};
return
saveFileDialog;
}
#endregion
#region Event Handlers
#region Context menu
private
void
OnToggleErrorMessagesClick(
object
sender, EventArgs e)
{
_isErrorsVisible = !_isErrorsVisible;
RedrawMessages();
}
private
void
OnToggleWarningMessagesClick(
object
sender, EventArgs e)
{
_isWarningsVisible = !_isWarningsVisible;
RedrawMessages();
}
private
void
OnToggleRegularMessagesClick(
object
sender, EventArgs e)
{
_isRegularsVisible = !_isRegularsVisible;
RedrawMessages();
}
private
void
OnContextMenuOpenning(
object
sender, CancelEventArgs e)
{
warningsVisibleToolStripMenuItem.Checked = _isWarningsVisible;
errorsVisibleToolStripMenuItem.Checked = _isErrorsVisible;
messagesVisibleToolStripMenuItem.Checked = _isRegularsVisible;
scrollToLastToolStripMenuItem.Checked = _scrollToLast;
copyThisMessageToolStripMenuItem.Visible = (_textArea.SelectedItem !=
null
);
}
private
void
OnClearLogClick(
object
sender, EventArgs e)
{
if
(ClearRequested !=
null
)
ClearRequested();
}
private
void
OnSaveAllToFileClick(
object
sender, EventArgs e)
{
SaveLogToFile(
false
);
}
private
void
OnSaveToClipboardClick(
object
sender, EventArgs e)
{
if
(SaveToClipboardRequested !=
null
)
SaveToClipboardRequested(VisibleMessageTypes);
}
private
void
OnSaveVisibleOnlyToFileClick(
object
sender, EventArgs e)
{
SaveLogToFile(
true
);
}
private
void
OnScrollToLastClick(
object
sender, EventArgs e)
{
_scrollToLast = !_scrollToLast;
}
private
void
OnCopySelectedMessageClick(
object
sender, EventArgs e)
{
var temp = _textArea.SelectedItem;
if
(temp !=
null
)
{
var text = ((TraceMessage)temp.Tag).MessageText;
Clipboard.SetText(text);
}
}
#endregion
#region ListView
void
OnTextAreaSizeChanged(
object
sender, EventArgs e)
{
_textArea.ItemSize =
new
Size(_textArea.Width - _textArea.ListViewElement.ViewElement.VScrollBar.Size.Width, 0);
}
private
void
OnTextAreaVisualItemFormatting(
object
sender, ListViewVisualItemEventArgs e)
{
e.VisualItem.TextWrap =
true
;
}
void
OnTextAreaVisualItemCreating(
object
sender, ListViewVisualItemCreatingEventArgs e)
{
e.VisualItem =
new
TracerListElement(WarningColor, ErrorColor, InfoColor, TimeStampColor, WarningFont, ErrorFont, InfoFont, TimeStampFont);
}
#endregion
protected
override
void
OnPaint(PaintEventArgs e)
{
base
.OnPaint(e);
_textArea.ItemSize =
new
Size(_textArea.Width - _textArea.ListViewElement.ViewElement.VScrollBar.Size.Width, 0);
}
#endregion
#region ITracerUIControl Implementation
public
event
Action ClearRequested;
public
event
Action<List<TraceMessage.MessageTypes>> SaveToClipboardRequested;
public
event
Action<
string
, List<TraceMessage.MessageTypes>> SaveToFileRequested;
public
void
AppendMessages(TraceMessage[] messages)
{
if
(InvokeRequired)
{
BeginInvoke(
new
Action<TraceMessage[]>(AppendMessages),
new
object
[] { messages });
return
;
}
var messagesNumber = messages.Length;
var newMessages =
new
List<ListViewDataItem>(messagesNumber);
var newVisibleMessages =
new
List<ListViewDataItem>();
for
(
int
i = 0; i < messagesNumber; i++)
{
var msg = messages[i].Copy();
var temp =
new
ListViewDataItem();
temp.Tag = msg;
temp.DataBoundItem = msg;
switch
(msg.Type)
{
case
TraceMessage.MessageTypes.Information:
temp.ForeColor = InfoColor;
temp.Font = InfoFont;
break
;
case
TraceMessage.MessageTypes.Warning:
temp.ForeColor = WarningColor;
temp.Font = WarningFont;
break
;
case
TraceMessage.MessageTypes.Error:
temp.ForeColor = ErrorColor;
temp.Font = ErrorFont;
break
;
}
newMessages.Add(temp);
if
(VisibleMessageTypes.Contains(msg.Type))
newVisibleMessages.Add(temp);
}
_messages.AddRange(newMessages);
_visibleMessages.AddRange(newVisibleMessages);
if
(_visibleMessages.Count > 0)
{
_textArea.BeginUpdate();
_textArea.Items.AddRange(newVisibleMessages.ToArray());
var lastIndex = _textArea.Items.Count - 1;
if
(_scrollToLast)
_textArea.SelectedItem = _textArea.Items[lastIndex];
_textArea.EndUpdate();
}
}
public
void
ClearLog()
{
_isErrorsVisible = _isRegularsVisible = _isWarningsVisible =
true
;
_messages.Clear();
_textArea.Items.Clear();
}
#endregion
class
TracerListElement : IconListViewVisualItem
{
public
TracerListElement(Color warningColor, Color errorColor, Color infoColor, Color timeMarkColor, Font warningFont, Font errorFont, Font infoFont, Font timeMarkFont)
{
_warningColor = warningColor;
_errorColor = errorColor;
_infoColor = infoColor;
_timeStampColor = timeMarkColor;
_warningFont = warningFont;
_errorFont = errorFont;
_infoFont = infoFont;
_timeMarkFont = timeMarkFont;
DrawBorder =
false
;
GradientStyle = GradientStyles.Solid;
}
#region Members
private
Color _warningColor;
private
Color _errorColor;
private
Color _infoColor;
private
Color _timeStampColor;
private
readonly
Font _warningFont;
private
readonly
Font _errorFont;
private
readonly
Font _infoFont;
private
readonly
Font _timeMarkFont;
private
LightVisualElement _timeStamp;
private
LightVisualElement _messageText;
private
LightVisualElement _link;
private
WrapLayoutPanel _stackLayout;
private
WrapLayoutPanel _linkStackLayout;
#endregion
protected
override
void
CreateChildElements()
{
base
.CreateChildElements();
_stackLayout =
new
WrapLayoutPanel { Orientation = Orientation.Horizontal, AutoSize =
true
, AutoSizeMode = RadAutoSizeMode.Auto };
_linkStackLayout =
new
WrapLayoutPanel { Orientation = Orientation.Vertical, AutoSize =
true
, AutoSizeMode = RadAutoSizeMode.Auto };
_timeStamp =
new
LightVisualElement { TextAlignment = ContentAlignment.TopLeft };
_messageText =
new
LightVisualElement { TextWrap =
true
, TextAlignment = ContentAlignment.TopLeft, AutoSize =
true
, AutoSizeMode = RadAutoSizeMode.Auto };
_link =
new
LightVisualElement { TextWrap =
true
, TextAlignment = ContentAlignment.TopLeft, ClickMode = ClickMode.Press };
_linkStackLayout.Children.Add(_messageText);
_linkStackLayout.Children.Add(_link);
_stackLayout.Children.Add(_timeStamp);
_stackLayout.Children.Add(_linkStackLayout);
Children.Add(_stackLayout);
}
//We override this property to erase unnecessary text that is displayed on custom item
public
override
string
Text
{
get
{
return
null
;
}
set
{
base
.Text = value;
}
}
protected
override
void
SynchronizeProperties()
{
base
.SynchronizeProperties();
// Let the base handle all hover/enter/selected events and efects
#region Forward child-element's mouse events to custom list item object
_timeStamp.ShouldHandleMouseInput =
false
;
_timeStamp.NotifyParentOnMouseInput =
true
;
_messageText.ShouldHandleMouseInput =
false
;
_messageText.NotifyParentOnMouseInput =
true
;
_link.ShouldHandleMouseInput =
true
;
_link.NotifyParentOnMouseInput =
true
;
_stackLayout.ShouldHandleMouseInput =
false
;
_stackLayout.NotifyParentOnMouseInput =
true
;
_linkStackLayout.ShouldHandleMouseInput =
false
;
_linkStackLayout.NotifyParentOnMouseInput =
true
;
NotifyParentOnMouseInput =
true
;
ShouldHandleMouseInput =
true
;
#endregion
var msg = (TraceMessage)Data.DataBoundItem;
_timeStamp.Text = msg.TimeStamp.ToString(
"HH:mm:ss"
);
_timeStamp.ForeColor = _timeStampColor;
_timeStamp.Font = _timeMarkFont;
switch
(msg.Type)
{
case
TraceMessage.MessageTypes.Information:
_messageText.ForeColor = _infoColor;
_messageText.Font = _infoFont;
break
;
case
TraceMessage.MessageTypes.Warning:
_messageText.ForeColor = _warningColor;
_messageText.Font = _warningFont;
break
;
case
TraceMessage.MessageTypes.Error:
_messageText.ForeColor = _errorColor;
_messageText.Font = _errorFont;
break
;
}
_messageText.Text = msg.MessageText;
_messageText.TextWrap =
true
;
_link.MouseDown -= OnLinkClick;
_link.Text = msg.Link;
_link.Font = _infoFont;
_link.ForeColor = Color.Blue;
if
(!
string
.IsNullOrEmpty(msg.Link))
_link.MouseDown += OnLinkClick;
}
void
OnLinkClick(
object
sender, EventArgs e)
{
Process.Start(((RadItem)sender).AccessibleName);
((RadItem)sender).ForeColor = Color.BlueViolet;
}
protected
override
Type ThemeEffectiveType
{
get
{
// What you return here is actually says how will your custom item behave.
// It will react to selection, hover and so on as Telerik's object of returned type.
return
typeof
(IconListViewVisualItem);
}
}
}
}
The code below is the class that is used as data item for list view visual item: