Hello,
I have an application were I include a Treeview.
I have some issues with my screen,
when I manipulate this treeview with drag and drop functionality.
The treeview get's rendered and is only showing a part.
It's like the scrollbar is not on the correct position.
When I scroll with the mouse up and down it will be rendered correct again.
This issue appear's when I reorder the treeview with the mouse, aswell in code.
It's not predictable when it occurs.
Has anyone an idea how to fix this ?
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
using Telerik.WinControls;
using Telerik.WinControls.Data;
using Telerik.WinControls.UI;
namespace SortAccountsSpike
{
public partial class SortAccountsForm : Form
{
private Object _currentTargetDataBoundItem;
public SortAccountsForm()
{
InitializeComponent();
// _dataList = new ObservableCollection<
AccountGroup
>();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
AutoScroll = false;
radTreeView1.TreeViewElement.NodeFormatting += TreeViewElement_NodeFormatting;
radTreeView1.DragStarting += RadTreeView1DragStarting;
radTreeView1.DragOverNode += RadTreeView1DragOverNode;
radTreeView1.DragEnding += RadTreeView1DragEnding;
radTreeView1.TreeViewElement.AutoSizeItems = true;
radTreeView1.ShowLines = true;
radTreeView1.ShowRootLines = true;
radTreeView1.ShowExpandCollapse = true;
radTreeView1.FullRowSelect = true;
radTreeView1.LineStyle = TreeLineStyle.Dot;
radTreeView1.MultiSelect = true;
radTreeView1.ShowNodeToolTips = true;
radTreeView1.TreeViewElement.Comparer = new AccounNodeComparer(radTreeView1.TreeViewElement);
radTreeView1.NodeExpandedChanging += radTreeView1_NodeExpandedChanging;
FillTreeView();
}
private void FillTreeView()
{
radTreeView1.ChildMember = @"AccountGroup\Accounts";
// radTreeView1.DisplayMember = @"GroupName\AccountNumber";
radTreeView1.SortOrder = SortOrder.Ascending;
radTreeView1.DataSource = AccountModel.AccountGroups;
}
void TreeViewElement_NodeFormatting(object sender, TreeNodeFormattingEventArgs e)
{
var treeNodeContentElement = e.NodeElement.ContentElement;
//General styling
/////////////
// treeNodeContentElement.StretchHorizontally = true;
treeNodeContentElement.Padding = new Padding(5);
treeNodeContentElement.Margin = new Padding(0, 2, 0, 2);
treeNodeContentElement.SmoothingMode = SmoothingMode.AntiAlias;
treeNodeContentElement.DrawBorder = true;
treeNodeContentElement.BorderColor = Color.FromArgb(110, 153, 210);
treeNodeContentElement.DrawFill = true;
treeNodeContentElement.GradientStyle = GradientStyles.Linear;
treeNodeContentElement.NumberOfColors = 2;
//AccountGroupFormatting
var accountGroup = e.Node.DataBoundItem as AccountGroup;
if (accountGroup != null)
{
treeNodeContentElement.Text = accountGroup.ToDisplayHtml();
e.Node.Expanded = accountGroup.Expanded;
//fill
treeNodeContentElement.BackColor = Color.FromArgb(248, 248, 248);
treeNodeContentElement.BackColor2 = Color.FromArgb(233, 233, 233);
}
//Account formatting
var accountInfo = e.Node.DataBoundItem as AccountInfo;
if (accountInfo != null)
{
treeNodeContentElement.Text = accountInfo.ToDisplayHtml();
if (accountInfo.IsVisible)
{
e.Node.ToolTipText = string.Empty;
treeNodeContentElement.BackColor = Color.FromArgb(174, 190, 217);
treeNodeContentElement.BackColor2 = Color.FromArgb(168, 183, 210);
treeNodeContentElement.ForeColor = Color.Black;
}
else
{
e.Node.ToolTipText = "This account is not visible in drop down lists.";
treeNodeContentElement.BackColor = Color.FromArgb(227, 233, 242);
treeNodeContentElement.BackColor2 = Color.FromArgb(235, 238, 245);
treeNodeContentElement.ForeColor = Color.FromArgb(153, 153, 153);
}
}
}
void radTreeView1_NodeExpandedChanging(object sender, RadTreeViewCancelEventArgs e)
{
var group = e.Node.DataBoundItem as AccountGroup;
if (group != null)
{
group.Expanded = !e.Node.Expanded;
}
}
void RadTreeView1DragStarting(object sender, RadTreeViewDragCancelEventArgs e)
{
//don't allow the first group to be moved
if (e.Node.Level == 0 && e.Node.Index == 0)
{
e.Cancel = true;
}
_currentTargetDataBoundItem = null;
}
void RadTreeView1DragOverNode(object sender, RadTreeViewDragCancelEventArgs e)
{
//determine if the dragged node can be dropped on the current hovered node
if (e.Node != null)
{
e.Cancel = !CanBeDroppedOnTarget(e.Node.DataBoundItem, e.TargetNode.DataBoundItem);
}
else
{
e.Cancel = true;
}
}
void RadTreeView1DragEnding(object sender, RadTreeViewDragCancelEventArgs e)
{
//recheck if it is correct to drop the dragged node on the target node (this event is fired multiple times when multiple nodes are dragged at once)
if (CanBeDroppedOnTarget(e.Node.DataBoundItem, e.TargetNode.DataBoundItem))
{
if (_currentTargetDataBoundItem == null)
_currentTargetDataBoundItem = e.TargetNode.DataBoundItem;
//execute drag operation
MoveNode(e.Node.DataBoundItem);
}
e.Cancel = true; //cancel event and make the treeview refresh to reflect the changed observable datasource
radTreeView1.Nodes.Refresh();
radTreeView1.SelectedNodes.Clear();
}
private bool CanBeDroppedOnTarget(Object draggedDataBoundItem, Object targetDataBoundItem)
{
var draggedGroup = draggedDataBoundItem as AccountGroup;
var targetGroup = targetDataBoundItem as AccountGroup;
if (draggedGroup != null)
{
//dragged node is a group -> target must also be a group
return (targetGroup != null);
}
else
{
//dragged node is an account
var draggedAccount = (AccountInfo)draggedDataBoundItem;
//-> if parent is also being dragged, the node cannot be dropped
var parentGroupIsAlsoDragged = radTreeView1.SelectedNodes.Select(n => n.DataBoundItem)
.OfType<
AccountGroup
>()
.Any(
group =>
group.Accounts.Any(
account => account.Id == draggedAccount.Id));
return !parentGroupIsAlsoDragged;
}
}
private void MoveNode(Object draggedDataBoundItem)
{
var draggedGroup = draggedDataBoundItem as AccountGroup;
var targetGroup = _currentTargetDataBoundItem as AccountGroup;
var draggedAccount = draggedDataBoundItem as AccountInfo;
var targetAccount = _currentTargetDataBoundItem as AccountInfo;
if (draggedGroup != null)
{
if (targetGroup == null)
{
throw new InvalidOperationException(
"Dropping an account group on a node that does not represent an account group is not possible.");
}
MoveAccountGroup(draggedGroup, targetGroup);
}
else
{
//move account
MoveAccount(draggedAccount, targetAccount, targetGroup);
}
}
private void MoveAccount(AccountInfo draggedAccount, AccountInfo targetAccount, AccountGroup targetGroup)
{
Debug.Assert(draggedAccount != null);
AccountGroup draggedGroup = AccountModel.AccountGroups.First(group => @group.Accounts.Contains(draggedAccount));
if (targetAccount != null)
{
targetGroup = AccountModel.AccountGroups.First(group => @group.Accounts.Contains(targetAccount));
}
Debug.Assert(targetGroup != null);
//move down items
var targetSequence = (targetAccount != null) ? targetAccount.Sequence : 0;
var accountInfosToMove = targetGroup.Accounts.Where(accInfo => accInfo.Sequence > targetSequence);
foreach (var accInfo in accountInfosToMove)
{
accInfo.Sequence += 1;
}
//move item
draggedAccount.Sequence = targetSequence + 1;
if (draggedGroup.Id != targetGroup.Id)
{
draggedGroup.Accounts.Remove(draggedAccount);
targetGroup.Accounts.Add(draggedAccount);
NormalizeAccountSequences(draggedGroup.Accounts);
}
NormalizeAccountSequences(targetGroup.Accounts);
if (_currentTargetDataBoundItem is AccountInfo)
{
_currentTargetDataBoundItem = draggedAccount;
}
}
private void MoveAccountGroup(AccountGroup draggedGroup, AccountGroup targetGroup)
{
//move down items
var groupsToMove = AccountModel.AccountGroups.Where(group => @group.Sequence > targetGroup.Sequence);
foreach (var group in groupsToMove)
{
@group.Sequence += 1;
}
//move group
draggedGroup.Sequence = targetGroup.Sequence + 1;
//normalize
NormalizeGroupSequences(AccountModel.AccountGroups);
}
private void NormalizeAccountSequences(IEnumerable<
AccountInfo
> accountInfosToNormalize)
{
var index = 0;
foreach (var accInfo in accountInfosToNormalize.OrderBy(account => account.Sequence))
{
accInfo.Sequence = ++index;
}
}
private void NormalizeGroupSequences(IEnumerable<
AccountGroup
> accountGroupsToNormalize)
{
var index = 0;
foreach (var group in accountGroupsToNormalize.OrderBy(group => group.Sequence))
{
group.Sequence = ++index;
}
}
private void MakeVisibleButtonClick(object sender, EventArgs e)
{
ChangVisibilityOfSelectedNodes(true);
}
private void MakeInvisibleButtonClick(object sender, EventArgs e)
{
ChangVisibilityOfSelectedNodes(false);
}
private void ChangVisibilityOfSelectedNodes(bool isvisible)
{
var accountsToChange = radTreeView1.SelectedNodes.Select(n => n.DataBoundItem).OfType<
AccountInfo
>().ToList();
foreach (var account in accountsToChange)
{
account.IsVisible = isvisible;
}
radTreeView1.Nodes.Refresh();
}
private void ChangeAliasButtonClick(object sender, EventArgs e)
{
string filledInAlias = string.Empty;
var uniquePreferedNames =
(from account in radTreeView1.SelectedNodes.Select(n => n.DataBoundItem).OfType<
AccountInfo
>()
group account by account.PreferedName
into accountGroup
select accountGroup.Key).ToList();
if (uniquePreferedNames.Count == 1)
{
filledInAlias = uniquePreferedNames.First();
}
var result = InputDialog.ShowInputBox("Edit alias", "Please fill in an alias for the selected account(s)", out filledInAlias, this, filledInAlias);
if (result == DialogResult.OK)
{
var accountsToChange = radTreeView1.SelectedNodes.Select(n => n.DataBoundItem).OfType<
AccountInfo
>().ToList();
foreach (var account in accountsToChange)
{
account.PreferedName = filledInAlias;
}
radTreeView1.Nodes.Refresh();
}
}
private static class AccountModel
{
private static ObservableCollection<
AccountGroup
> _accountGroups;
public static ObservableCollection<
AccountGroup
> AccountGroups
{
get { return _accountGroups ?? (_accountGroups = LoadInitialDataList()); }
}
private static ObservableCollection<
AccountGroup
> LoadInitialDataList()
{
var dataCollection = new ObservableCollection<
AccountGroup
>();
var groupNone = new AccountGroup
{
Id = Guid.NewGuid(),
GroupName = "No group",
Sequence = 1,
Expanded = true
};
dataCollection.Add(groupNone);
var groupTest = new AccountGroup
{
Id = Guid.NewGuid(),
GroupName = "Some other group",
Sequence = 2
};
dataCollection.Add(groupTest);
var anotherGroup = new AccountGroup
{
Id = Guid.NewGuid(),
GroupName = "Yet another group",
Sequence = 3
};
dataCollection.Add(anotherGroup);
var item1 = new AccountInfo
{
Id = Guid.NewGuid(),
AccountNumber = "1",
Balance = 154000,
Currency = "EUR",
Product = "Alba VZW, Mullens Kris",
Sequence = 1,
IsVisible = true
};
groupNone.Accounts.Add(item1);
var item2 = new AccountInfo
{
Id = Guid.NewGuid(),
AccountNumber = "2",
Balance = 500,
Currency = "EUR",
Product = "John Doe",
PreferedName = "The unknown account",
Sequence = 3,
IsVisible = true
};
groupNone.Accounts.Add(item2);
var item3 = new AccountInfo
{
Id = Guid.NewGuid(),
AccountNumber = "3",
Balance = -600,
Currency = "EUR",
Product = "Jane Doe",
Sequence = 4
};
groupTest.Accounts.Add(item3);
var item4 = new AccountInfo
{
Id = Guid.NewGuid(),
AccountNumber = "4",
Balance = -600,
Currency = "EUR",
Product = "John Denver",
Sequence = 5
};
groupTest.Accounts.Add(item4);
var item5 = new AccountInfo
{
Id = Guid.NewGuid(),
AccountNumber = "5",
Balance = 600897,
Currency = "EUR",
Product = "Richie Vallens",
Sequence = 6,
IsVisible = true
};
groupTest.Accounts.Add(item5);
for (int i = 0; i < 10; i++)
{
var item6 = new AccountInfo
{
Id = Guid.NewGuid(),
AccountNumber = "BE11 2222 3333 4444",
Balance = -150,
Currency = "EUR",
Product = "Donna Summer",
Sequence = i + 1
};
anotherGroup.Accounts.Add(item6);
}
return dataCollection;
}
}
}
public class AccounNodeComparer : TreeNodeComparer
{
public AccounNodeComparer(RadTreeViewElement treeViewElement)
: base(treeViewElement)
{
}
public override int Compare(RadTreeNode x, RadTreeNode y)
{
//compare 2 account groups
var accountGroup1 = x.DataBoundItem as AccountGroup;
var accountGroup2 = y.DataBoundItem as AccountGroup;
if (accountGroup1 != null && accountGroup2 != null)
{
return accountGroup1.CompareTo(accountGroup2);
}
//compare 2 accounts
var accountInfo1 = x.DataBoundItem as AccountInfo;
var accountInfo2 = y.DataBoundItem as AccountInfo;
if (accountInfo1 != null && accountInfo2 != null)
{
return accountInfo1.CompareTo(accountInfo2);
}
return 0;
}
}
}
Kind regards.
Ralf