The sender of the event is the object inside the treeviewitem. However, it's not a dependencyobject so I don't think I can use ItemFromContainer to get the actual TreeViewItem. What should I do to get the actual treeviewitem that is selected?
i see that everyone is as confused by my question as I am by my problem.
according to the linked site, I can get my treeviewitem from the selecteditem property. However, when I attempt to do this I get null because the sender is a SignalViewModel object instead of a TreeViewItem.
TreeViewItem treeViewItem = this.AvailableSignalsTreeView.SelectedItem as TreeViewItem;
this returns null for me but the debugger shows that the selected item is of type SignalViewModel.
All I'm trying to do is get multiselect capability for a treeview, which I was going to do using an example I saw that just toggles the treeviewitems.
<TreeView
Grid.Row="0"
Background="Blue"
Foreground="Orange"
x:Name="AvailableSignalsTreeView"
SelectedItemChanged="AvailableSignalsTreeView_SelectedItemChanged"
ItemsSource="{Binding Source={StaticResource available_signals_source}}"
>
<TreeView.CommandBindings>
<CommandBinding Command="ApplicationCommands.SelectAll"
Executed="AvailableSignalsTreeView_SelectAll"
CanExecute="AvailableSignalsTreeView_SelectAllCanExecute"/>
</TreeView.CommandBindings>
<TreeView.InputBindings>
<KeyBinding
Command="ApplicationCommands.SelectAll"
Modifiers="Ctrl"
Key="A"
/>
</TreeView.InputBindings>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource ="{Binding Path = bits}" >
<TextBlock
Text="{Binding Path = SignalName}"
/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Path = BitNumber}"
/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Bits to Signal" Click="AddBitToSignal" />
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
Try this:
// Helper to search up the VisualTree
private static T FindAnchestor<T>(DependencyObject current)
where T : DependencyObject
{
do
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
}
while (current != null);
return null;
}
private void AvailableSignalsTreeView_SelectedItemChanged(
object sender,
RoutedPropertyChangedEventArgs<Object> e)
{
var treeViewItem = FindAnchestor<TreeViewItem>((DependencyObject)e.OriginalSource);
}
I think what I was thinking of is this. Basically the selected item isn't a dependency object so I can't traverse the tree using them, and I have to instead get the container using the itemcontainer generator.
I thought I tried this yesterday, not sure why it wasn't working.
TreeViewItem tvi = tv.ItemContainerGenerator.ContainerFromItem(tv.SelectedItem) as TreeViewItem;
Related
My application uses TreeView populated with custom nodes defined in TreeView.ItemTemplate. Content of each node is wrapped into StackPanel with Node_ContexMenuOpening event that populates context menu based on some application properties, which is working.
XAML:
<TreeView x:Name="treeNodes" ContextMenu="{StaticResource EmptyContextMenu}" ContextMenuOpening="TreeNodes_ContextMenuOpening">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type c:MyCustomType}" ItemsSource="{Binding MyCustomTypeChildren}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource EmptyContextMenu}" ContextMenuOpening="Node_ContextMenuOpening" >
<Image Source="Frontend\Images\import.png" MaxWidth="15" MaxHeight="15"/>
<TextBlock Width="5"/>
<TextBlock Text="{Binding CustomTypeName}" MinWidth="100"/>
<TextBlock Width="10"/>
<Image Source="CustomImagePath" MaxWidth="15" MaxHeight="15"/>
<TextBlock Width="5"/>
<TextBlock Text="{Binding CustomTypeName2}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Code behind:
private void Node_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
FrameworkElement fe = sender as FrameworkElement;
// get context menu and clear all items (empty menu with single placeholder item
// is assigned in XAML to prevent "no object instance" exception)
ContextMenu menu = fe.ContextMenu;
menu.Items.Clear();
// populate menu there
}
I would like to have same functionality on TreeView (treeview specific context menu when right clicking on empty area of treeview), which also works.
private void TreeNodes_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
TreeView tw = sender as TreeView;
ContextMenu menu = tw.ContextMenu;
menu.Items.Clear();
// poopulate menu there
}
But the issue is that TreeNodes_ContextMenuOpening is fired even after right clicking at TreeView node, right after Node_ContextMenuOpening is handled, which overwrites context menu for clicked node. I tried to solve it using:
// also tried IsMouseOver and IsMouseCaptureWithin
if (tw.IsMouseDirectlyOver)
{
// handle TreeNodes_ContextMenuOpening event there
}
but without success. Any suggestions?
Thanks in advance.
You can try using the ContextMenuEventArgs.Handled value. https://learn.microsoft.com/en-us/dotnet/api/system.windows.routedeventargs.handled?view=netcore-3.1#System_Windows_RoutedEventArgs_Handled
Gets or sets a value that indicates the present state of the event handling for a routed event as it travels the route.
Example
protected override void OnPreviewMouseRightButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true; //suppress the click event and other leftmousebuttondown responders
MyEditContainer ec = (MyEditContainer)e.Source;
if (ec.EditState)
{ ec.EditState = false; }
else
{ ec.EditState = true; }
base.OnPreviewMouseRightButtonDown(e);
}
I need to check what treeviewitem the user selected everytime it changes. I used the SelectedItemChangedmethod or whatever this is called. It works fine but now I need to do a switch case for every possible treeviewitem. But I can't do that since I have no idea how to get the name of it. I checked on internet but some people said to use treeview.SelectedItem but it returns System.Windows.Controls.TreeViewItem Header: Items.Count:0. I was wondering if I could do that entirely in the .cs code file or if I had to use data binding and such.
Thanks for your help.
EDIT:
Here is how i setup the treeview and the treeviewitems. They are all like the second example.
<TreeView x:Name="treeview" Margin="10,10,0,4" HorizontalAlignment="Left" Width="192" Background="#FFA45353" SelectedItemChanged="treeview_SelectedItemChanged"">
<TreeViewItem IsExpanded="False">
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="./Resources/smallicons/icon.jpg"/>
<TextBlock Text=" Main" FontSize="14"/>
</StackPanel>
</TreeViewItem.Header>
<!--==============================================================================================-->
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="First" Foreground="Black" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
<!--==============================================================================================-->
Are you looking for this?
TreeViewItem selectedNode = (TreeViewItem)treeView.SelectedItem;
string strSelectedNode = selectedNode.Header.ToString();
The above snippet assumes a few things though.
By the way, this code is part of
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem selectedNode = (TreeViewItem)treeView.SelectedItem;
MessageBox.Show(selectedNode.Header.ToString());
}
Ok...As per your treeview and treeview Item setup, you have to code something like this..
private void treeview_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
TreeViewItem selectedNode = (TreeViewItem)treeview.SelectedItem;
var sp = selectedNode.Header as StackPanel;
var tb = sp.Children[0] as TextBlock;
var selecteditem = tb.Text;
//MessageBox.Show(selecteditem);
switch (selecteditem)
{
case "Main":
MessageBox.Show(selecteditem);
break;
case "First":
MessageBox.Show(selecteditem);
break;
default:
MessageBox.Show("no matching item found");
break;
}
}
Here, I am digging in the selected tree view item to get the Text of selected tree view item. You may have to tweak the code a bit to get it fully operational. Let me know if you need any help with it.
I know this was already asked a lot, but I didn't find any solution.
My ListView looks like this
<ListView Margin="0,0,0,0" x:Name="ContactListView" BorderBrush="Black" ItemsSource="{Binding RosterItemX}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="25">
<Image Tag="{Binding Availability}" Margin="0,0,5,0" Width="16" Height="16" VerticalAlignment="Center">
</Image>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<Custom:EventToCommand Command="{Binding ContactDblClicked, Mode=OneWay}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
I have my ICommand in my viewmodel:
public ICommand ContactDblClicked { get { return new RelayCommand<MouseButtonEventArgs>(contactDblClicked); } }
This event fires everytime someone double clicks into the ListView. Doesn't have to be on a ListViewItem.
I can handle the case when no ListViewItem is selected.
I cast (ListView)e.Source, and check if an Item is selected.
I need a way to check if what is double clicked is actually a ListViewItem and not empty space.
Not entirely sure what you mean by your last line I need a way to check if what is double clicked is actually a ListViewItem and not empty space., but here are two suggestions:
First, if you want to check if an item is selected in your ListView:
private void contactDblClicked(MouseButtonEventArgs obj)
{
var listView = obj.Source as ListView;
if (listView != null)
{
if (listView.SelectedItem != null)
{
Debug.WriteLine("item selected");
}
else
{
Debug.WriteLine("item not selected");
}
}
}
However, I think you already got that solution and if I understand your question right, you want to check if the user actually clicked an item (and not whitespace) even if an item is selected.
So here is the second approach to check if an item was really clicked:
private void contactDblClicked(MouseButtonEventArgs obj)
{
if (((FrameworkElement) obj.OriginalSource).DataContext is YourRosterItemXType)
{
Debug.WriteLine("item was *really* clicked");
}
}
Where YourRosterItemXType is the type of your binded RosterItemX property. With the above code you check, if the DataContext of the original source of the mouse event is set to YourRosterItemXType. Items in your ListView have that DataContext set and so you check if that mouse event comes really from one of those list items.
I am looking to implement a treeview inside a combobox. Basically I want it to show as a combobox when collapsed but a treeview inside combo box when expanded. When a user clicks on a node, I want it to show in the collapsed combo box. I have got this working so far.
The problem I am having is that how do I show a default value from c# when this combo box is loaded. Please help guys as I am running out of ideas :)
Thanks in advance.
Data Template
<DataTemplate x:Key="TreeViewExpanded" >
<StackPanel>
<TreeView x:Name="DPointTree" Margin="5" ItemsSource="{Binding Datapoint}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Field}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding = "{Binding Path=SelectedFieldName, Mode=TwoWay}" Value = "">
<Setter Property = "Visibility" Value = "Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Here is the Xaml
<ComboBox Name="cmbFieldName" Width="150" Background="White" ItemsSource="{Binding}" SelectedItem="{Binding SelectedFieldName , Mode=TwoWay}" >
<ComboBox.ItemTemplateSelector>
<local:TreeViewSelector/>
</ComboBox.ItemTemplateSelector>
</ComboBox>
Here is the DataSet passed to this.
Datapoint _datapoint2 = new Datapoint();
_datapoint2.Name = "Alpha";
_datapoint2.FieldID.Add("Contains Elements");
_datapoint2.Field.Add("1 Year");
_datapoint2.Field.Add("2 Year");
_datapoint2.Field.Add("3 Year");
Try this:
private void TreeView_Loaded(object sender, RoutedEventArgs e)
{
TreeView areaTreeView = sender as TreeView;
// Expand the treeview
if (areaTreeView != null)
{
ExpandCollapseTreeNodes(areaTreeView, true);
}
}
public static void ExpandCollapseTreeNodes(ItemsControl parentContainer, bool isExpanded)
{
foreach (Object item in parentContainer.Items)
{
TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (currentContainer != null) // && currentContainer.Items.Count > 0
{
//expand the item
currentContainer.IsExpanded = isExpanded;
//if the item's children are not generated, they must be expanded
if (isExpanded && currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
//store the event handler in a variable so we can remove it (in the handler itself)
EventHandler eh = null;
eh = new EventHandler(delegate
{
//once the children have been generated, expand those children's children then remove the event handler
if (currentContainer.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
ExpandCollapseTreeNodes(currentContainer, isExpanded);
currentContainer.ItemContainerGenerator.StatusChanged -= eh;
}
});
currentContainer.ItemContainerGenerator.StatusChanged += eh;
}
//otherwise the children have already been generated, so we can now expand those children
else
{
ExpandCollapseTreeNodes(currentContainer, isExpanded);
}
}
}
}
it is not pretty but works to select a node in the tree
// in code behind after the tree is declared, e.g. after InitializeComponent();
SynchronizationContext.Current.Post(delegate
{
// expand the tree to the selected node after loading
treeView.ExpandAll(); // or if path is known treeView.ExpandPath(...);
SynchronizationContext.Current.Post(delegate
{
treeView.GetContainerFromItem(root.Children[0]).IsSelected = true;
}, null);
}, null);
I've got a ListBox in a WP7 App where I want to do something with an item when the user hold it. The event work's great. My hold method gets called, but I can't detect which element in the list was hold.
ListBox.SelectedItem is always -1 and a code from another post on stackoverflow doens't work:
FrameWorkelement element = (FrameworkElement) e.OriginalSource;
ItemViewModel item = (ItemViewModel) element.DataContext;
I get an InvalidCastException when running it in the second line.
The following code should work.
private void StackPanel_Hold(object sender, GestureEventArgs e)
{
ItemViewModel itemViewModel = (sender as StackPanel).DataContext as ItemViewModel;
string t = itemViewModel.LineOne;
}
Note: before using the DataContext of the sender object, make sure you cast the sender object to the correct class. In this example I use a StackPanel in my DataTemplate:
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Height="78" Hold="StackPanel_Hold">
<TextBlock Text="{Binding LineOne}" />
<TextBlock Text="{Binding LineTwo}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>