I am using a wpf tree in a .net form. So, I don't have any xaml. I simply do everything in code. I am using Hierarchical Data Template to bind my data to the wpftree.
I am trying to find a way to get the TreeViewItem for the Node selected in the tree. I tried registering a EventHandler on the SelectedItemChanged event on the TreeView, but in that handler I only get the associated data object. Since my tree is virtual, ItemContainerGenerator.ContainerFromItem doesn't work.
When I searched on StackOverflow, one suggestion was to listen to the TreeViewItem.Selected event.
But I couldn't find a way to do this in code. ( I don't have a xaml).
Any help is greatly appreciated.
thank you.
What you could do is attach the handler to each control every time you add it
void AddTreeViewItem()
{
TreeView t = new TreeView();
TreeViewItem treeItem = new TreeViewItem();
t.Items.Add(treeItem);
treeItem.Selected += DoSomethingHere;
}
private void DoSomethingHere(object sender, RoutedEventArgs e)
{
Console.WriteLine("Tree Item Selected");
}
Related
I have a treeview and im trying to implement a drag and drop functionallity.
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="TreeViewItem.PreviewMouseLeftButtonDown" Handler="PreviewMouseLeftButtonDown" />
</Style>
</TreeView.ItemContainerStyle>
Then i have an event in the code behind to handle the click, and start the drag:
void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is TreeViewItem)
{
//clone the tree item, create an adorner, apply adorner
//doDragDrop
}
}
My problem is since PreviewMouseLftButtonDown is a routed event using the tunneling strategy, when the mouse is clicked on a treenode item, I get the root node first. I am not interested in the root node, only the leaf nodes. My structure is like this:
HeaderNodeObject
|_LeafnodeObject
|_LeafNodeOject
So in my event I need to basically not do anything and just proceed if the sender treeViewItem is a HeaderNodeObject. But how can i tell what it is? the sender comes in as a treeViewItem, im not sure how to tell what the object it holds is.
You should be able to use e.Source as TreeViewItem to retrieve the item that was clicked. You can check it against sender to confirm that you only process the event when it has reached the source.
This should result in the event only being processed for the clicked item:
void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.Source is TreeViewItem item && sender == item)
/* do something */;
}
Note, however, that using the event so bluntly means that you'll receive the event if any part of the item receives a mouse down event, including the expansion button. Thus, be careful to *not* set e.Handled = true, lest you prevent the user from toggling expansion, among other things.
The above solution only works for leafs, and I had to do this for a TreeView where I wanted to process the PreviewMouseLeftDown event on any node in the tree. I finally solved it by ignoring the event for all nodes where a child had mouse over. Like this:
var treeViewItem = sender as TreeViewItem;
if (treeViewItem.Items.OfType<TreeViewItem>().All(item => !item.IsMouseOver))
{
//do my stuff
}
Sort of a hackish solution, but I couldn't find any better, and at least it gets the job done...
I solved this fairly simply by just checking if the itemsource on the sender is null. If its null that means its a leaf node:
if (sender is TreeViewItem)
{
Item = sender as TreeViewItem;
//make sure we have a leaf node, if we dont just move on.
if (Item.ItemsSource == null)
{
//do my stuff
}
}
Will not call this a duplicate answer but will simply mention that Øystein Kolsrud's answer is as correct as this can be without data binding. I don't have enough reputation or else I would simply comment and up-vote, but "User's" answer will only work for leafs on the tree, while Øystein's answer works for all nodes on the tree, including all the leaf nodes.
My case involved going through a fairly shallow (yet heavily populated) tree of recursively populated 'work items.' I was using a TreeView to display the work flow hierarchy, where a selected node would raise an event that captured the Work ID text from the header of the TreeViewItem, so I could then be able to use that WorkID in a meaningful way. When I wanted this to happen for more than just the root or just the first level of children (leaving out the root), Øystein's answer is the way I had to go. Definitely hackish, but definitely effective.
All of the following code I had (besides his conditional check), was there previously. All I had to do was include his check and everything worked as I initially intended.
Just wanted to share a real-world example of this working and verify his work. This condition works like a charm and saved my butt. Thanks again, Øystein Kolsrud!!
private void TreeItem_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem workItem = sender as TreeViewItem;
if(workItem.Items.OfType<TreeViewItem>().All(item => !item.IsMouseOver))
{
string workItemHeader = workItem.Header.ToString();
string stopAt = "-";
currentWorkIDToEdit = workItemHeader.Substring(0, workItemHeader.IndexOf(stopAt));
workItemToEdit.Content = "Work Item To Edit: " + currentWorkIDToEdit + " ";
}
}
I have a TreeView in which I would like to allow the user to add and delete subitems from. In exploring basic functionality I am using a button and a textbox to add this subitem. When the user clicks on the button a new TreeViewItem needs to be created and set as a subitem of my parent TreeView with the text from the textbox set as the subitem's Header.
This is my current code under the button_click event:
//ADD T_ITEM TO PARENT TREEVIEW
private void button1_Click(object sender, RoutedEventArgs e)
{
TreeViewItem item = new TreeViewItem();
item.Header = textBox1.Text;
//Compiler does not recognize "Nodes"
Parent.Nodes.Add(item);
}
Specifically, the compiler has a problem with Nodes. The main question that I've used to help me makes a lot of sense, but just doesn't work for me. All of the sources I have looked at uses the Nodes command at one time or another with no problem. Do I need to include a reference, or is my code completely off?
--This guide uses System.Windows.Forms; in order to use Nodes, but doesn't seem to help because I am using Windows Presentation Foundation.
Please show me how to get my code working in the right direction.
Thank you.
I did some more research and found the equivalent method for adding child TreeViewItems to parent TreeViewItems in WPF.
This is the change I made to my code:
//ADD T_ITEM TO PARENT TREEVIEW
private void button1_Click(object sender, RoutedEventArgs e)
{
TreeViewItem item = new TreeViewItem();
item.Header = textBox1.Text;
Parent.Items.Add(item);
}
I am using LongListSelector control on Windows Phone 8 and can't figure out the best way to handle a tap on an item.
The few examples I've found rely on the SelectionChanged event. However, this solution is buggy because if I tap an item that opens a new page, hit back, and then tap the same item again, it won't work because this item is already selected, so SelectionChanged is not triggered.
I tried to register to the tap event and use the current selected item as the tapped one, but some times the current selected item is not the one I expect.
I could wrap my ItemTemplate in a button and handle the tap for each item but I need to reskin the button to make it look like a simple list item.
Finally, I don't understand why it is so complicated to achieve such a basic thing. Is there a simple and standard way I missed?
My second wish is to get an effect on the item when it is tapped. Is there any standard way to do it?
You could null your LongListSelector's SelectedItem at the end of each SelectionChanged event. I.e.
<phone:LongListSelector x:Name="LLS" SelectionChanged="LLS_SelectionChanged">
And the event handler:
private void LLS_SelectionChanged(object sender, SelectionChangedEventArgs e) {
// If selected item is null, do nothing
if (LLS.SelectedItem == null)
return;
// Navigate to the next page
NavigationService.Navigate(new Uri("/nextpage.xaml", UriKind.Relative));
// Reset selected item to null
LLS.SelectedItem = null;
}
You'll fire the SelectionChanged event twice, but nothing's going to happen the second time round and you should get the behaviour that you're looking for - (i.e Setting SelectedItem to null will trigger a new SelectionChanged event, but this second event gets caught in the if-statement)
As for the second part of your question, you might be better posting a new question.
I done it with the Tap event handling.
I prefer not to use Selected property, but get tapped item this way (and I haven't noticed any bugs):
MyListItemClass item = ((FrameworkElement)e.OriginalSource).DataContext
as MyListItemClass;
Also, you could get the original item ContentPresenter simple by navigating up through VisualTree from e.OriginalSource. That way:
ContentPresenter itemPresenter = SomeHelperClass
.FindParent<ContentPresenter>(e.OriginalSource,"");
Where FindParent is similar to find child in this question: How can I find WPF controls by name or type?
ContentPresenter is that object what you need to manually change the item template if you want to (to set "selected" state for example).
private void Item_tap(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
DataSource data = (DataSource)element.DataContext;
}
My second wish is to get an effect on the item when it is tapped. Is
there any standard way to do it?
For this the only thing you need to do add this to your control (or stackpanel where you want to have this effect):
<StackPanel toolkit:TiltEffect.IsTiltEnabled="True">
first add this to *.xaml page inside the
LongListSelectorSelectionChanged="listBox_SelectionChanged"
so that it looks like this :
<toolkit:LongListSelector x:Name="listBox" SelectionChanged="listBox_SelectionChanged">
then in the *.xaml.cs file in the event handler
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Write your logic on what you want to do with the selected item
}
In addition to halil´s answer:
First of all you need to install the Windows Phone Toolkit (WPtoolkit) by NuGet.
Than add the namespace declaration on the PhoneApplicationPage.
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
After this you can add toolkit:TiltEffect.IsTiltEnabled="True" to the control definition.
It is nice documneted by NOKIA:
http://developer.nokia.com/community/wiki/Tilt_Effect_for_Windows_Phone
Oliver
i have a TreeView which is binded trough a HierarchicalDataTemplate. Im listing there a List which contains a list of a directories and files.
private void getDirectoryList()
{
using (FileOperations fileOP = new FileOperations())
{
this.DokumentBrowser.ItemsSource = fileOP.list_directory(rpath); //Liste wird an den DokumentenBrowser gebunden
selectedOrdner = (Ordner)DokumentBrowser.Items.GetItemAt(0);
this.FileBrowser.DataContext = selectedOrdner.FileName;
}
}
The selectedOrdner buffers the Ordner Object which is actual selected by the user in the TreeView. If its empty it is set to the item at position 0 which is the root directory. When now the user puts a new file in the TreeView
runMethodtoCopyFile
getDirectoryList();
TreeViewItem tvi = (TreeViewItem)DokumentBrowser.ItemContainerGenerator.ContainerFromItem(DokumentBrowser.Items.CurrentItem);
tvi.IsExpanded = true;
tvi.Focus();
im runnning a method to copy the file and then im reading the new directory structure and want to set the expand property to true from the last treeviewitem which was selected. Because my TreeView was always showing me my rootnode when i put a new file or dir into it.
The Buffer of the selectedOrdner contains as long as im not putting a new file into it the Ordner Object. But when im running the method to copy a file it no longer contains an Object from type Ordner but treeviewitem instead.
I dont get it where it is changed from type Ordner to type TreeViewItem.
edit-----
The error always happens when the TreeView SelectedItemChanged happen.
private void DokumentBrowser_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
selectedOrdner = (Ordner)DokumentBrowser.SelectedItem;
FileBrowser.ItemsSource = selectedOrdner.FileName;
}
When I try to add something to the rootnode it works fine it only are the nodes after the first where it starts to break.
edit2------
There was a problem when binding the treeview to a new itemsource that the selecteditemchange method has a disconnecteditem and that could not be castet to a (Ordner)
solved this by making all changes over an observable collection. So the TreeViewItem is Expanded is not longer needed
Bring XAML to light please! ))
At the current state of inquiry it is hard to say something. May be problem in binding somewhere in your HierarchicalDataTemplate or it might be another piece of your codebehind.The only i can say now - you'd better check all your references from/to SelectedItem both in WPF XAML and C# code parts.
P.S.: Very strange by the way - usually people are confused with counterpart problem, how to get TreeViewItem for SelectedItem.
I am using ASP.NET with C# 2.0 and Visual Studio 2005. I am using a Master page and content pages. I have a treeview menu in the master page and when a user selects any menu item I redirect to that content page.
My problem is that after a user navigates to the content page all the treenodes refresh and the structure is collapsed. I want the selected treenode to stay expanded.
Can anybody help me out?
When you refresh the treeview you want to call treeView1.ExpandAll();
Also add an event for the BeforeCollapse and set the event's Cancel property to true, to prevent the user from collapsing your treenodes.
private void treeView1_BeforeCollapse(object sender, TreeViewCancelEventArgs e)
{
e.Cancel = true;
}
Hope this helps.
-jeremy
This is a common enough problem that is automatically handled by ASP.NET if you use a SiteMapDataSource control as the datasource for your TreeView. In this case, you haven't mentioned what the Datasource of your TreeView is.
You also haven't mentioned if the TreeView contains links (the NavigateUrl property is set) or Text items that postback for each click. If it is the former, then as far as I know, you are out of luck! This is because none of the Selection events are raised for TreeNodes which have their NavigateUrl set. They just function as regular hyperlinks.
If however, it is the latter, then you can try out the following steps :
a. Handle the SelectedNodeChanged event of the TreeView. In this event handler, retrieve the current value of the SelectedNode.ValuePath property and store it in ViewState/Session. Use the Value of the of the SelectedNode to conditionally redirect the page to URL mapped to it.
Something like the following:
protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
{
TreeNode selNode = TreeView1.SelectedNode;
string pathToNode = selNode.ValuePath;
Session.Add("SelPath", pathToNode);
switch (selNode.Value)
{
//Redirect to URL accordingly.
}
}
b. On subsequent load of the Master page (the page to which you redirected), retrieve the value of the ValuePath set earlier and find the previously Selected node and Expand it.
Something like the following:
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
string pathToNode = (string)Session("SelPath");
Session.Remove("SelPath");
TreeNode selNode = TreeView1.FindNode(pathToNode);
if (selNode != null)
{
selNode.Expand();
}
}
}
Note that I haven't had an opportunity to test the code so this is mostly hypothetical.
Try using the OnTreeNodeDataBound event and the treeView.SelectedNode property
Also, might want to check how/ when you're binding your TreeView to it's DataSource. You might be rebinding it on IsPostBack which will re-render the tree.
The TreeView should maintain its nodes on PostBack.
Even though you are using a Master page, once the user navigates to the content page it is rendered as a new/different page. Because of the Master page the same treeview is loaded but not the same instance. You will need to store and load what nodes were expanded.