WPF - Treeview selected item index - c#

I have a treeview panel. In the panel, there are several child nodes. Some of them are only a header.
The way I create the treeview:
treeviewpaneL.Items.Add(art);
art.Items.Add(prt);
some if statement....
TreeViewItem cldhdr = new TreeViewItem() { Header = "ChildNodes:" };
prt.Items.Add(cldhdr);
TreeViewItem cld = new TreeViewItem() .......
........
.....
cldhdr.Items.Add(cld);
Treeview:
Node1
ChildNodes: (This is header only. It appears if child node exists)
Childnode1
Childnode2
childnode3
Node2
Node3
ChildNodes:
Childnode1
Childnode2
childnode3
Node4
Node5
In my treeview there are also images in front of all nodes. It's a code driven treeview. In the xaml part i have only:
<TreeView x:Name="treeviewpaneL" SelectedItemChanged="treeviewpaneL_SelectedItemChanged" >
</TreeView>
What I want to do is when I click on any of the treeview items, I need to get its index number.
My code is:
private void treeviewpaneL_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
int index = 0;
ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(prt);
foreach (var _item in parent.Items)
{
if (_item == treeviewpaneL.SelectedItem)
{
selectedNodeIndex = index;
MessageBox.Show(selectedNodeIndex.ToString());
break;
}
index++;
}
}
With the code above, I can get the index of Node1,Node2,Node3, Node4 and Node5 as 0,1,2,3,4
What I want is to get the index numbers as:
Node1 = 0
Childnode1 = 1 (Skipping the header)
Childnode2 = 2
Childnode3 = 3
Node2 = 4
....
....
....
What am I missing?

Here is the solution, first of all your "MyTreeViewItem"
public class MyTreeViewItem :TreeViewItem
{
private int _index;
public int Index
{
get { return _index; }
set { _index = value; }
}
public MyTreeViewItem() : base()
{
}
}
and usage;
MyTreeViewItem art = new MyTreeViewItem();
art.Header = "Node1";
art.Index = 1;
MyTreeViewItem prt = new MyTreeViewItem();
prt.Header = "Child1";
prt.Index = 2;
art.Items.Add(prt);
treeviewpaneL.Items.Add(art);
and event;
private void treeviewpaneL_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
MyTreeViewItem selectedItem = e.NewValue as MyTreeViewItem;
if (selectedItem != null)
{
MessageBox.Show("" + selectedItem.Index);
}
}

To get the index of the currently selected item:
MyTreeView.Items.IndexOf(MyTreeView.SelectedItem);

Related

StackOverflowException in recursive ASP.NET treeview population

I have a custom ASP.NET treeview control, which uses existing MS Treeview. I create and recreate the treeview upon postbacks (it is in UpdatePanel) from a stored IEnumerable.
Some items are added like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack && !Page.IsAsync)
{
DD.Items = null;
DD.Items.Add(new TreeviewItem("Choice 1", "4", "-1"));// = items;
DD.Items.Add(new TreeviewItem("something", "1", "-1"));
DD.Items.Add(new TreeviewItem("Europe", "2", "-1"));
DD.Items.Add(new TreeviewItem("pff", "3", "-1"));
}}
The control is initialized and loaded in it's OnLoad using BuildTreeFromItemCollection():
public void BuildTreeFromItemCollection()
{
BuildTreeFromItemCollection(this.Items, null);
}
public void BuildTreeFromItemCollection(IEnumerable<StoredItem> items, TreeNode parentNode)
{
IEnumerable<TreeviewItem> tvItems = items.Cast<TreeviewItem>();
var nodes = tvItems.Where(x => parentNode == null ? int.Parse(x.Parent) <= 0 : x.Parent == parentNode.Value);
TreeNode childNode;
foreach (var i in nodes)
{
childNode = new TreeNode(i.Name, i.Value)
{
PopulateOnDemand = this.PopulateOnDemand
};
if (parentNode == null)
{
TvHierarchy.Nodes.Add(childNode);
}
else
{
parentNode.ChildNodes.Add(childNode);
}
this.BuildTreeFromItemCollection(items, childNode);
}
}
TreeNodePopulate is handled like so:
void TvHierarchy_TreeNodePopulate(object sender, TreeNodeEventArgs e)
{
this.EnsureChildControls();
IEnumerable<StoredItem> childItems = NodePopulator(e.Node.Value);
foreach (StoredItem item in childItems)
{
TreeNode newNode = new TreeNode(item.Name, item.Value);
newNode.PopulateOnDemand = this.PopulateOnDemand;
e.Node.ChildNodes.Add(newNode);
}
this.Items.AddRange(childItems);
}
and this Func is temporarily attached to the NodePopulator:
private IEnumerable<StoredItem> ItemLoader(string val)
{
List<StoredItem> itemList = new List<StoredItem>();
Random r = new Random();
for (int i = 0; i <= 4; i++)
{
int rand = r.Next(10,100);
itemList.Add(new TreeviewItem("test " + rand.ToString(), rand.ToString(), val));
}
return itemList;
}
Unfortunately the BuildTreeFromItemCollection falls into infinite loop after a couple of node expansions, around 4th level, and i'm left with stack overflow.
The exact exception shows up on the line
var nodes = tvItems.Where(x => parentNode == null ? int.Parse(x.Parent) <= 0 : x.Parent == parentNode.Value);
but the Call stack looks already filled up. Where's the problem?
So it seems that the whole problem was in the code generating new nodes in this test function:
int rand = r.Next(10,100);
itemList.Add(new TreeviewItem("test " + rand.ToString(), rand.ToString(), val));
So when an ID was generated which already was earlier the code went into infinite loop. After expanding range to (10,10000) everything works fine.

Trying to search ListView for subitems matching a string

I'm having trouble scanning through a ListView to locate a subitem matching a given string. Here's my code:
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
string date = datePicker.Value.ToShortDateString();
int count = Program.booker.listView.Items.Count;
for (int i = 0; i < count; i++)
{
ListViewItem lvi = Program.booker.listView.Items[i];
if (lvi.SubItems.Equals(date))
{
MessageBox.Show("Found!", "Alert");
Program.booker.listView.MultiSelect = true;
Program.booker.listView.Items[i].Selected = true;
}
else
{
MessageBox.Show("Nothing found for " + date, "Alert");
}
}
}
The ListView is located on the Booker form, and I'm accessing it from the Filter class. I'd like to search the entire ListView for any items matching my date string. Thanks!
You can use the FindItemWithText method.
ListViewItem searchItem = null;
int index = 0;
do
{
if (index < Program.booker.listView.Items.Count)
{
//true = search subitems
//last false param = no partial matches (remove if you want partial matches)
searchItem = Program.booker.listView.FindItemWithText(date, true, index, false);
if (searchItem != null)
{
index = searchItem.Index + 1;
//rest of code
}
}
else
searchItem =null;
} while (searchItem != null);

Moving an item up and down in a WPF list box

I have a list box with a bunch of values in it. I also have an UP button and a DOWN button.
With these buttons, I would like to move the selected item in the list box up/down. I am having trouble doing this.
Here is my code so far:
private void btnDataUp_Click(object sender, RoutedEventArgs e)
{
int selectedIndex = listBoxDatasetValues.SelectedIndex; //get the selected item in the data list
if (selectedIndex != -1 && selectedIndex != 0) //if the selected item is selected and not at the top of the list
{
//swap items here
listBoxDatasetValues.SelectedIndex = selectedIndex - 1; //keep the item selected
}
}
I do not know how to swap the values! Any help would be GREATLY appreciated!
Since you have populated the listbox by binding to a ObservableCollection using ItemsSource, you cant modify the Items property of the listbox.
ItemsSource can be set only when the Items collection is empty, and Items can be modified only when ItemsSource is null.
Otherwise you will get the error "Operation is not valid while ItemsSource is in use..."
What you have to do, is modify the underlying collection, and because it's an ObservableCollection, the ListBox will reflect the changes.
The following code shows how you can move an item up and down by swapping the item in the collection.
The corresponding XAML just contains a listbox called lbItems and 2 buttons which hook up the eventhandlers.
public partial class MainWindow : Window
{
private ObservableCollection<string> ListItems = new ObservableCollection<string>
{
"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"
};
public MainWindow()
{
InitializeComponent();
lbItems.ItemsSource = this.ListItems;
}
private void up_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex > 0)
{
var itemToMoveUp = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex - 1, itemToMoveUp);
this.lbItems.SelectedIndex = selectedIndex - 1;
}
}
private void down_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.ListItems.Count)
{
var itemToMoveDown = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}
}
I make some extension methods for this:
public static void MoveItemUp<T>(this ObservableCollection<T> baseCollection, int selectedIndex)
{
//# Check if move is possible
if (selectedIndex <= 0)
return;
//# Move-Item
baseCollection.Move(selectedIndex - 1, selectedIndex);
}
public static void MoveItemDown<T>(this ObservableCollection<T> baseCollection, int selectedIndex)
{
//# Check if move is possible
if (selectedIndex < 0 || selectedIndex + 1 >= baseCollection.Count)
return;
//# Move-Item
baseCollection.Move(selectedIndex + 1, selectedIndex);
}
public static void MoveItemDown<T>(this ObservableCollection<T> baseCollection, T selectedItem)
{
//# MoveDown based on Item
baseCollection.MoveItemDown(baseCollection.IndexOf(selectedItem));
}
public static void MoveItemUp<T>(this ObservableCollection<T> baseCollection, T selectedItem)
{
//# MoveUp based on Item
baseCollection.MoveItemUp(baseCollection.IndexOf(selectedItem));
}
There is no need to know the ListBox for this.
This is the easiest way to do this and it fires all the right events so you don't have to worry about XAML. ObservableCollection has a nice method called
MoveItem(previousIndex, newIndex)
Given that you have a ObservableCollection named DataItemList
public void MoveUp()
{
var currentIndex = DataItemList.SelectedIndex;
//Index of the selected item
if (currentIndex > 0)
{
int upIndex = currentIndex - 1;
//move the items
DataItemList.MoveItem(upIndex,currentIndex);
}
}
For Down you get the index of the preceding item.
Simple as that!
I would have added a comment, but I can't since I only have 3 reputation :/
Peter Hansen's solution is great, but if there isn't a selected element, down_click throws an ArgumentOutOfRange Exception. This is because if there is no element selected, the Index is equal to -1.
I'd edit down_click like this:
private void down_click(object sender, RoutedEventArgs e)
{
if (this.lbItems.SelectedIndex != -1) //Added condition
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.ListItems.Count)
{
var itemToMoveDown = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}
}
try this:
if (listBoxDatasetValues.SelectedItems.Count > 0)
{
object selected = listBoxDatasetValues.SelectedItem;
int indx = listBoxDatasetValues.Items.IndexOf(selected);
int totl = listBoxDatasetValues.Items.Count;
if (indx == 0)
{
listBoxDatasetValues.Items.Remove(selected);
listBoxDatasetValues.Items.Insert(totl - 1, selected);
listBoxDatasetValues.SetSelected(totl - 1, true);
}
else{
listBoxDatasetValues.Items.Remove(selected);
listBoxDatasetValues.Items.Insert(indx - 1, selected);
listBoxDatasetValues.SetSelected(indx - 1, true);
}
}
if(listBoxDatasetValues.ListIndex > 0)
{
// add a duplicate item up in the listbox
listBoxDatasetValues.AddItem(listBoxDatasetValues.Text, listBoxDatasetValues.ListIndex - 1);
// make it the current item
listBoxDatasetValues.ListIndex = (listBoxDatasetValues.ListIndex - 2);
// delete the old occurrence of this item
listBoxDatasetValues.RemoveItem(listBoxDatasetValues.ListIndex + 2);
}
You may try something like this:
For moving UP:
if (listboxName.SelectedIndex == -1 || listboxName.SelectedIndex == 0)
return;
Object select, previous, temp;
select = listboxName.Items[listboxName.SelectedIndex];
previous = listboxName.Items[listboxName.SelectedIndex-1];
temp = select;
select = previous;
previous = temp;
listboxName.Items[listboxName.SelectedIndex] = select;
listboxName.Items[listboxName.SelectedIndex-1] = previous;
listboxName.SelectedIndex--;
For moving Down:
if (listboxName.SelectedIndex == -1 || listboxName.SelectedIndex == listboxName.Items.Count-1)
return;
Object select, next, temp;
select = listboxName.Items[listboxName.SelectedIndex];
next = listboxName.Items[listboxName.SelectedIndex+1];
temp = select;
select = next;
next = temp;
listboxName.Items[listboxName.SelectedIndex] = select;
listboxName.Items[listboxName.SelectedIndex+1] = next;
listboxName.SelectedIndex++;
No need to use observable collection you can do it simpler just by relying on the collection object inside the ListBox, here is the optimized version of #Peter Hansen`s answer:
private void up_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex > 0)
{
var itemToMoveUp = this.lbItems.Items[selectedIndex];
this.lbItems.Items.RemoveAt(selectedIndex);
this.lbItems.Items.Insert(selectedIndex - 1, itemToMoveUp);
this.lbItems.SelectedIndex = selectedIndex - 1;
}
}
private void down_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.lbItems.Items.Count)
{
var itemToMoveDown = this.lbItems.Items[selectedIndex];
this.lbItems.Items.RemoveAt(selectedIndex);
this.lbItems.Items.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}

Find the level of Deepest child Treenode

I have treenode & i would like to find the deepest child in the treenode.
if there are 2 child nodes with level 11 & level 13 respectively then i need unction to return me the value 13.
How can i do that ?
public int FindLevel(TreeNode oParentNode)
{
counter++;
forech(TreeNode oSubNode in oParentNode.Nodes)
{
FindLevel(oParentNode);
}
return Counter;
}
Here is my suggestion for you:
private int GetDeepestChildNodeLevel(TreeNode node)
{
var subLevel = node.Nodes.Cast<TreeNode>().Select(GetDeepestChildNodeLevel);
return subLevel.Count() == 0 ? 1 : subLevel.Max() + 1;
}
here with explicit types:
private int GetDeepestChildNodeLevel(TreeNode node)
{
var subLevel = node.Nodes.Cast<TreeNode>().Select<TreeNode, int>(subNode => GetDeepestChildNodeLevel(subNode));
return subLevel.Count<int>() == 0 ? 1 : subLevel.Max() + 1;
}
Here's the extension method for TreeView that returns Level of the deepest node in that TreeView.
public int GetDeepestNodeLevel(this System.Windows.Forms.TreeView treeView)
{
int level = -1;
foreach (System.Windows.Forms.TreeNode node in treeView.Nodes) {
int deep = DigInNodes(node);
if (deep > level)
level = deep;
}
return level;
}
private int DigInNodes(System.Windows.Forms.TreeNode node)
{
int level = node.Level;
foreach (System.Windows.Forms.TreeNode subnode in node.Nodes) {
int deep = DigInNodes(subnode);
if (deep > level)
level = deep;
}
return level;
}
Code is tested and works for me.
This is the fast way to get levelĀ“s deep of selected node.
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
TreeNode node = treeView1.SelectedNode;
MessageBox.Show(string.Format("You selected: {0}{1}", node.Text,e.Node.Level));
}

C# help treeview and checkbox content

I really can't get out of this one.
I got treeview items in treeviews. The treeview items contain checkboxes with content. How do i get the content and put it in a list.
currently i got this
foreach (TreeViewItem item in treeView1.Items)
{
foreach (TreeViewItem childItem in item.Items)
{
CheckBox checkBoxTemp = childItem.Header as CheckBox;
if (checkBoxTemp == null) continue;
optieListBox.Items.Add(checkBoxTemp.Content);
}
}
I am not sure if i get your question correctly, but you can try this.
foreach (TreeViewItem childItem in item.Items)
{
CheckBox cbx = null;
//finds first checkbox
foreach(object child in childItem.Items){
cbx = child as CheckBox;
if (cbx != null) break;
}
ctrList.Items.Add(cbx.Content);
}
Bind your TreeView to a collection instead. That way you won't have to manipulate UI components to access the data, you will access the data directly.
The other way to do this is through recursion: Declare optieListBox list at class level and call GetContainers() method as an entry point call. optieListBox list should give you content list for all checked items in treeview.
List<string> optieListBox = new List<string>();
private List<TreeViewItem> GetAllItemContainers(TreeViewItem itemsControl)
{
List<TreeViewItem> allItems = new List<TreeViewItem>();
for (int i = 0; i < itemsControl.Items.Count; i++)
{
// try to get the item Container
TreeViewItem childItemContainer = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem;
// the item container maybe null if it is still not generated from the runtime
if (childItemContainer != null)
{
allItems.Add(childItemContainer);
List<TreeViewItem> childItems = GetAllItemContainers(childItemContainer);
foreach (TreeViewItem childItem in childItems)
{
CheckBox checkBoxTemp = childItem.Header as CheckBox;
if (checkBoxTemp != null)
optieListBox.Items.Add(checkBoxTemp.Content);
allItems.Add(childItem);
}
}
}
return allItems;
}
private void GetContainers()
{
// gets all nodes from the TreeView
List<TreeViewItem> allTreeContainers = GetAllItemContainers(this.objTreeView);
// gets all nodes (recursively) for the first node
TreeViewItem firstNode = this.objTreeView.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
if (firstNode != null)
{
List<TreeViewItem> firstNodeContainers = GetAllItemContainers(firstNode);
}
}
Try this:
List<string> values = new List<string>;
foreach (string node in treeView.Nodes)
{
values.Add(node);
}
//Loop through nodes
Also, if the tree view's nodes have children (nodes), try this instead:
List<string> values = new List<string>;
//Called by a button click or another control
private void getTreeValues(Object sender, EventArgs e)
{
foreach (string node in treeView.Nodes)
{
TreeNode child = (TreeNode)child;
values.Add(node)
getNodeValues(child);
}
foreach (string value in values)
{
Console.WriteLine(value + "\n");
}
}
//Recursive method which finds all children of parent node.
private void getNodeValues(TreeNode parent)
{
foreach (string child in parent.Nodes)
{
TreeNode node = (TreeNode)child;
values.Add(child);
if (nodes.Nodes.Count != 0) getNodeValues(child);
}
}

Categories