I'm using DevExpree XtraTreeList Control, I want to randomly Set one of the first levels nodes to be the first node in the Tree, nothing helpful shown in the TreeList Control's Methods nor in the TreeListNode Methods,
Please Advice.
Edit: My Code
private void btnSetMaster_ButtonClick(object sender, DevExpress.XtraEditors.Controls.ButtonPressedEventArgs e)
{
//Load reprot
if (treeLstRprtDS.FocusedNode != null)
{
treeLstRprtDS.SetNodeIndex(treeLstRprtDS.FocusedNode,0);
//Get selected underlying object
ReportDataSource rprtDataSourceSelected =
(ReportDataSource)treeLstRprtDS.GetDataRecordByNode(treeLstRprtDS.FocusedNode);
theReport.SetReportDataSourceAsMaster(rprtDataSourceSelected);
}
}
Edit:
Note: working on bound mode
Solution:
I implemented the CompareNodeValues Event for the XtrTreeList Control
Read here...
and then forced the tree to do sorting using Column.SortIndex Read here...
Do you wish to scroll the TreeList so that a certain node to be the top one? If so, use the TreeList's TopVisibleNodeIndex property. If you need a certain node to be the first one, you should sort the TreeList within its CompareNodeValues event handler.
It sounds like you're looking for the SetNodeIndex method.
Related
I'm trying to actually deselect all nodes in my TreeView. By "actually", I mean that TreeView1.SelectedNode = null; will actually deselect the node in the tree.
Right now, visually speaking, the node is deselected. However, when I try to add a new node, the treeview will automatically select the first node in the tree (at the top) and create a subnode when ideally I want to create a parent node. I can't just deselect the selected node before adding either, because the user might want to add a child node. The behaviour I'd like is for the parent/child node adding to be based on what is selected in the treeview. If nothing is selected, add a parent, if something is selected, add a child in that selected node.
I construct a TreeNode object called node in a function with images and text and all that then I have the following:
if (tvContent.SelectedNode == null)
tvContent.Nodes.Add(node);
else
{
tvContent.SelectedNode.Nodes.Add(node);
tvContent.SelectedNode.Expand();
}
I have a "Deselect All" button that is supposed to make the above code work. The code for that button is simply:
tvContent.SelectedNode = null;
Pardon my tagging both C# and VB.NET. I'm good with both so if someone can help me out in either language that'd be terrific!
Thanks
EDIT:
Interesting. It seems that while testing if the selected node is null, .NET will automatically set the selected node to the first node in the tree. The following code shows the "trigger" message box, but immediately selects the first node in the tree after the if statement is complete.
private void btnDeselectAll_Click(object sender, EventArgs e)
{
tvContent.SelectedNode = null;
if (tvContent.SelectedNode == null) MessageBox.Show("trigger");
}
EDIT2: The issue lies in using an InputBox for the title input of the node. For whatever reason, that changes the selected node of the treeview. I tried this in a stock project and managed to replicate the issue. I guess there's no fixing this :S
So it turns out that getting "true" deselection isn't possible. As soon as the treeview loses focus then gains focus again (e.g. via an inputbox window popping up), the selected node will no longer be null.
My work around was to introduce a panel that becomes visible with some input options so that node title input is done on the main form instead of on another form. I don't like this fix but it's all that can be done.
This worked for me
Private LastSelectetNode As TreeNode
Protected Overrides Sub OnBeforeSelect(e As TreeViewCancelEventArgs)
e.Cancel = LastSelectetNode Is Nothing
MyBase.OnBeforeSelect(e)
End Sub
Protected Overrides Sub OnMouseUp(e As MouseEventArgs)
Dim nd = MyBase.HitTest(e.Location).Node
If LastSelectetNode Is nd Then
SelectedNode = Nothing
LastSelectetNode = Nothing
Else
LastSelectetNode = nd
End If
MyBase.OnMouseUp(e)
End Sub
I tried to reproduce your scenario but failed. After setting SelectedNode to null, it remained null for me when trying to read it back. A few things I want to check on:
Are you sure you're actually deselecting the node? If you have the "HideSelection" property of the TreeView set to True (default), the selection disappears anytime the TreeView loses focus (like when you click your deselect all button - making it look like it's working). Make sure this isn't the case by setting HideSelection to False.
Are you sure you're not triggering an event handler (like SelectedNodeChanged) when you set the SelectedNode to null?
I'd like to programmatically emulate a click on a node in a TreeView control. There's no clickable method as far I can see (something corresponding to other controls) and I guess that I need to go for the currently selected node.
So I've tried the following:
christmasTreeView.SelectedNode. ???
However, intellisense gave me no hint on what to call in order to fire a clickety-click on the node. How can it be done?
You can do something like:
// find the node you want to select and make it the SelectedNode
christmasTreeView.SelectedNode = christmasTreeView.Nodes[1]; // <-- the index you need
// Now trigger a select
christmasTreeView.Select();
// or
//christmasTreeView.Focus();
This will fire:
private void christmasTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
// awesome
}
Possible approach (not very smooth, though).
TreeNode preSelected = ChristmasTreeView.SelectedNode;
ChristmasTreeView.SelectedNode = null;
ChristmasTreeView.SelectedNode = preSelected;
ChristmasTreeView.Select();
Your main issue is that a Windows Forms TreeNode does not derive from a Control like a TreeView does (or, for example, a Button). It's much closer to a "model" class, meaning that it's primarily concerned with the hierarchical organization of your data. Although some of the presentational abstraction is leaked in properties like Color, Bounds, Handle and similar, a TreeNode doesn't know how to paint itself, nor how to handle click events.
On the other hand, a TreeView is an actual Control, meaning you can derive from it and be able to override its protected OnClick method, like shown in the example you linked.
If you want to follow that path, you could create your derived TreeView class from it and override the protected OnNodeMouseClick method. This method is specific to the TreeView and called by its WndProc method when a certain node is clicked.
But having read your comments to other answers, it seems that this is not what you really need to do to accomplish your goal.
You need to use event handler for TreeView.NodeMouseClick.
This Event got parameter which You can call in Your EventHandler like below:
void MyTreeview_NodeMouseClick(object sender,
TreeNodeMouseClickEventArgs e)
{
// do something with e.Node
}
I am building a searchable TreeView (treeView1), which will remove any TreeNode that do not contain the search keyword. However, I would like to reset the tree back to its original structure on the click of a button. I am storing the original treeview in a variable before the search is performed.
TreeView originalTreeView = new TreeView();
originalTreeView = treeView1;
I then perform the search, which is likely to remove some TreeNodes.
treeView1.searchTree(keyword);
Then, when I click the Reset button, I would like treeView1 to once again show the full originalTreeView, which is what I cannot figure out how to do.
private void resetBtn_Click(object sender, EventArgs e) {
treeView1 = originalTreeView;
}
This code does nothing (the listener is OK). Is it possible to do something like this, or do I have to populate the tree all over again every time?
Your code are assign the reference to originalTreeView only, but not copy/clone as a new object (backup).
Please try this Copy all treeView parent and children to another treeView c# WinForms
In my program I have a UserControl that contains a TreeView. That TreeView has a ViewModel and a Model relating to it. I would like to make it so that by clicking buttons, I can shift nodes up and down throughout the tree. This is similar to what one might implement on a listBox.
As a guide, I am using this article.
I am implementing the following functions into the code-behind of the UserControl for which the TreeView exists.
//Move up
private void moveUp_Click(object sender, RoutedEventArgs e)
{
if(UCViewModel.TreeView.SelectedItem != null)
{
if(UCViewModel.TreeView.SelectedItem is TreeModel)
{
TreeModel tm = UCViewModel.TreeView.SelectedItem as TreeModel;
if(tm.Rank != 1)
{
}
}
}
}
private void MoveUp(TreeModel tm)
{ //My guess on how to call the equivalent command...
foreach (TreeModel item in // **UCViewModel.TreeView.GetAllChildren....? )
{
}
}
Because my structure is different, and I am actually implementing an ObservableCollection as a TreeView, I do not have access to the same methods as the code in the example.
The following lines are the lines that I am concerned about...
TreeView.Items();
TreeView.Items.Clear();
TreeView.Items.Add();
How can I make the equivalent calls with the way my TreeView is setup? Please let me know if more code would be helpful.
The main idea of MVVM is not to use anything like treeView.Items.Add() or treeView.GetAllChildren() or whatever method you need from TreeView.
MVVM Pattern says you dont care about View and you dont know about the View or any control inside the View.
Therefore if you have an ObservableCollection as ItemsSource in your ViewModel you just need to move items there and the TreeView will follow you.
As simple as that. Your TreeView just needs to know where the ObservableCollection is placed inside your ViewModel.
Whenever you change something inside ObservableCollection you trigger collection changed event with appropriate event arguments holding information whether you added new items or shifted items around. That is how TreeView will know what to do.
I have a subclass of System.Windows.Forms.TreeView that's manually "bound" to a set of hierarchical data. I want the user to be able to edit the labels of the tree, and have the changes reflected back to the data. So I set LabelEdit to true and overrode OnAfterLabelEdit to the tune of:
protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
base.OnAfterLabelEdit(e);
TreeNode node = e.Node;
if (PassesSomeValidation(e.Label))
{
MyDataNode dataNode = node.Tag as MyDataNode;
dataNode.SomeBoundValue = e.Label;
int oldIndex = node.Index;
int newIndex = RepositionChangedDataNode(dataNode);
TreeNode parent = node.Parent;
parent.Nodes.RemoveAt(oldIndex);
parent.Nodes.Insert(newIndex, node);
}
else
{
e.CancelEdit = true;
}
}
RepositionChangedDataNode() re-sorts the data and returns the index into which the change node moved after sorting. I was hoping I could simply move the edited node to reflect this move.
The problem is that this causes the node to stay in edit mode! I've tried calling EndEdit(), cloning the node before inserting it, setting LabelEdit to false and back to true, wrapping the change in BeginUpdate()/EndUpdate(), and various combinations of these ideas, but none of them have any effect.
The culprit seems to be the insertion. Even if I attempt to insert a totally new node, it will go into edit mode immediately.
So, is there any way to make TreeView not behave this way? And if not, is there a good workaround?
Some ideas I've considered:
Set a custom TreeViewNodeSorter. Would prefer not to have to sort my data twice, though.
Set a flag and delay the remove-insert step until some point after AfterLabelEdit. It works to do it during WndProc, but this feels like a big kludge that is likely to fail somehow.
Use BeginInvoke() to push the remove-insert step back onto the message queue like so:
BeginInvoke(new MethodInvoker(delegate(
{
parent.Nodes.RemoveAt(oldIndex);
parent.Nodes.Insert(newIndex, node);
}));
This works and seems cleaner to me than #2, but I know this is probably not how BeginInvoke() was intended to be used, and that it may have repercussions that my very limited knowledge of the message pump cannot predict.
If you set LabelEdit for the TreeView to false, newly added nodes will not be in edit mode.
You just have to handle the case where the user wants to edit a label: Create a handler for the MouseClick event of the TreeView, where you get the clicked node by location. Set LabelEdit to true and call BeginEdit(). At the end of your handler for the AfterLabelEdit event (and after calling EndEdit(...) at an appropriate point), set LabelEdit to false again.
This works for me, whereas the solution with BeginInvoke only changed which node was in edit mode at the end.
try create a global variable, let's say:
private bool _allowEdit;
initialize it to true,
in your OnAfterLabelEdit method set it to false after your changes:
... int oldIndex = node.Index;
int newIndex = RepositionChangedDataNode(dataNode);
TreeNode parent = node.Parent;
parent.Nodes.RemoveAt(oldIndex);
parent.Nodes.Insert(newIndex, node);
**_allowEdit = false;**
}
else ...
then capture the OnBeforeLabelEdit event like this:
protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
base.OnBeforeLabelEdit(e);
e.CancelEdit = !_allowEdit;
_allowEdit = true;
}
I noticed that just after 'AfterLabelEdit' is fired, 'BeforeLabelEdit' is refired. That's why you have to stop it right there.
If you're using databinding, shouldn't the update to the data source (SomeBoundValue) trigger a refresh of the nodes? Maybe you can force the currency manager to repopulate the tree view.... If you're worried about performance you could use one of the sorting algorithms that works well with data that's almost already sorted (e.g., NOT quicksort - merge or heapsort come to mind)
Or you could dispense with data binding entirely and manually handle the repositioning since you're already half way there with RepositionChangedDataNode()....
You could try to unhook your OnEdit handler before adding the new node and re-hooking it after. I've seen that behavior before and that's how I handled it.