I have a WPF combobox...which is non-editable. When I tab into this combobox...I have a style setter (<Setter Property="IsDropDownOpen" Value="True"/>) to open the combobox. But when I tab again..the focus move to next item in the opened combobox....and it cycles over there. I am not able to tab out to next control.
What is wrong here?
Thanks
Try :
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsTabStop" Value="False"/>
</Style>
Or
Work with KeyboardNavigation :
WPF tab order with custom controls?
Not recommend, but works...
<Grid>
<ComboBox Grid.Row="1" Margin="0,0,0,0" Name="comboBox1" HorizontalAlignment="Left" Width="120" Height="20" IsEditable="False" KeyDown="comboBox1_KeyDown" GotKeyboardFocus="comboBox1_GotKeyboardFocus" >
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsDropDownOpen" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
<ComboBoxItem>Male</ComboBoxItem>
<ComboBoxItem>Female</ComboBoxItem>
<ComboBoxItem>Unknown</ComboBoxItem>
</ComboBox>
</Grid>
private void comboBox1_KeyDown(object sender, KeyEventArgs e)
{
ComboBox cb = sender as ComboBox;
if (e.Key == Key.Tab && cb.IsDropDownOpen)
{
ComboBoxItem item = FocusManager.GetFocusedElement(Window.GetWindow(this)) as ComboBoxItem;
cb.SelectedItem = item;
cb.IsDropDownOpen = false;
e.Handled = true;
}
}
private void comboBox1_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
ComboBox cb = sender as ComboBox;
cb.IsDropDownOpen = true;
}
You can achieve the same by the following code:-
private void comboBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
IsDropDownOpen = true;
e.Handled = true;
}
}
Now when focus will set on ComboBox then you need to hit enter to open drop down and you can use down key word to traverse ComboBox items.
To move to the next control,you need to press tab.
I had the same problem, solved it like this in XAML:
<Style x:Key="RadComboBoxItemStyle" TargetType="telerik:RadComboBoxItem">
<Setter Property="Focusable"
Value="False" />
<Setter Property="IsHitTestVisible"
Value="True" />
Related
I´ve tried to attach a event to the pencil icon and it works but only with the row that is selected in that moment even when is not the one where the pencil is pressed, I want that the event occurs only pressing the pencil icon and not depending which row is selected.
This is the creation of the column with the pencil icon and the event for a stackpanel
<DataGridTemplateColumn Header="cinco" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding id4}" Foreground="#2ECBC6" FontSize="20" Margin="11,5" TextTrimming="WordEllipsis" />
<StackPanel PreviewMouseDown="StackPanel_PreviewMouseDown" HorizontalAlignment="Right">
<materialDesign:PackIcon Kind="LeadPencil" Foreground="#2ECBC6" Width="30" Height="30"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#2ECBC6"/>
<Setter Property="FontWeight" Value="Medium" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="18"/>
<Setter Property="Margin" Value="15,0" />
</Style>
</DataGridColumn.HeaderStyle>
</DataGridTemplateColumn>
This is the method that is called when the pencil icon is pressed
private void StackPanel_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
test test1 = new test();
test1 = (test)ListNameTest.SelectedItem;
if (test1 == null)
{
MessageBox.Show("seleccionar fila");
}
else
{
var dato = test1.id4;
Content_OrdenTrabajo.Text = dato.ToString();
Host_OrdenTrabajo.IsOpen = true;
Keyboard_OrdenTrabajo.KeyboardState = Rife.Keyboard.KeyboardState.Numeric;
test1.id4 = Content_OrdenTrabajo.Text;
}
}
The sender parameter of the StackPanel_PreviewMouseDown method will contain the StackPanel on which the event was raised.
Since this StackPanel is a cell element, its DataContext will inherit from the cell.
And the DataContext of the cell is the collection item for which this row was created.
Example:
private void StackPanel_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var stackPanel = (StackPanel)sender;
var rowData = stackPanel.DataContext;
}
To get property values, you need to add a rowData cast to the item type of the DataGrid.ItemsSource collection.
test test1 = (test) ((FrameworkElement)sender).DataContext;
In my legacy project I need to get the binding property name via code behind. The XAML:
<DataGridTextColumn MinWidth="180" MaxWidth="180" Width="Auto" Binding="{Binding ConfigObject.MAC_Descr}" Header="Descr" Foreground="Black">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Padding" Value="6,12" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirty}" Value="True">
<Setter Property="TextBlock.Background" Value="{StaticResource IsDirtyColor}" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Background" Value="White"/>
<Setter Property="Padding" Value="5,12"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
The event I use:
private void Row_DoubleClick(object sender, MouseButtonEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
DataGridCell row = sender as DataGridCell;
if (row == null) return;
// Binding column name??
string bindingExpression = row.GetBindingExpression(TextBlock.TextProperty).ResolvedSourcePropertyName;
}
}
Basically in the case above I need to retrieve the "MAC_Descr". Any help?
You could try something like this:
private void OnCellDoubleClick(object sender, MouseButtonEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
if (!(sender is DataGridCell cell)) return;
if (!(cell.Column is DataGridTextColumn column)) return;
if (!(column.Binding is Binding binding)) return;
var path = binding.Path.Path;
}
}
Cast the Content of the cell to a TextBox or TextBlock depending on whether the cell is editable:
DataGridCell row = sender as DataGridCell;
if (row == null) return;
TextBox textBox = row.Content as TextBox;
if (textBox == null) return;
string bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty)
.ResolvedSourcePropertyName;
Code:
<Button Style="{StaticResource HPForegroundStyle}" IsTabStop="False"
Command="{Binding ForegroundPhoneCommand}" Click="Button_Click">
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding OptionsMenuItemList}" ItemContainerStyle="{StaticResource ContextMenuItemStyle}"
IsOpen="{Binding IsMenuOpen}"
PlacementTarget="{Binding RelativeSourc={RelativeSource AncestorType={x:Type Button}}}">
</ContextMenu>
</Button.ContextMenu>
</Button>
i am using MVVM pattern. In ViewModel i have a property 'IsMenuOpen' which controls context menu open close .Problem is that i'm able to disable right click and not able to show context menu on left click.
You can check also ContextMenuService.IsEnabled attached property on the parent control. It will block only right click and you will still be able to show the menu manually on left click, so based on previous example:
<Button x:Name="btn" Click="btn_Click" ContextMenuService.IsEnabled="false">
<Button.ContextMenu>
<ContextMenu x:Name="popup">
...
</ContextMenu>
</Button.ContextMenu>
</Button>
private void btn_Click(object sender, RoutedEventArgs e)
{
popup.Visibility = Visibility.Visible;
popup.IsOpen = true;
}
This worked for me using XAML similar to the question.
private bool _isMenuOpen = false;
public bool IsMenuOpen
{
get { return _isMenuOpen; }
set
{
// Don't allow the UI (right-click) to set this property to true
if (!value)
_isMenuOpen = value;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
_isMenuOpen = true;
btn.ContextMenu.IsOpen = true;
}
A few things to keep track of:
Ensure that the DataContext of the ContextMenu is valid.
Make sure that IsOpen is a TwoWay binding.
If you're going to attempt left click opening, remember that the PlacementTarget is not valid, so you'll have to set Button.ContextMenu.PlacementTarget = this, and then IsMenuOpen = true to have it show up.
My code snippet for reference:
<Style x:Key="SubjectButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource CommandButtonStyle}">
<Setter Property="Foreground" Value="Green" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext.Manager, RelativeSource={RelativeSource Self}}"
ItemsSource="{Binding SubjectManager.ContextMenuItems}"
IsOpen="{Binding SubjectManager.ContextMenuIsOpen, Mode=TwoWay}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding OnClick}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="DarkGreen" />
</Trigger>
</Style.Triggers>
</Style>
And in the view model:
public void ShowContextMenu(SearchCondition searchCondition, Button button)
{
button.ContextMenu.DataContext = this;
SubjectManager.OpenContextMenu();
}
If you want to bind a menu to a property, consider the Popup control. It has similar functionality to the context menu but is not bound to a particular mouse button...
<Popup IsVisible = {Binding IsMenuOpen} >
<!-- Fill in what you need here -->
</Popup>
You can do it just like that:
<Button x:Name="btn" Click="btn_Click" MouseRightButtonDown="btn_MouseRightButtonDown">
<Button.ContextMenu>
<ContextMenu x:Name="popup" Visibility="Collapsed">
<MenuItem Header="aaa"></MenuItem>
<MenuItem Header="bbb"></MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
private void btn_Click(object sender, RoutedEventArgs e)
{
popup.Visibility = Visibility.Visible;
popup.IsOpen = true;
}
private void btn_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
popup.Visibility = Visibility.Collapsed;
}
I have a style defined for my ListBoxItems with a trigger to set a background color when IsSelected is True:
<Style x:Key="StepItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="Border" Padding="0" SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="#40a0f5ff"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This style maintains the selected item even when the ListBox and ListBoxItem loses focus, which in my case is an absolute must.
The problem is that I also want the ListBoxItem to be selected when one of its TextBox's child gets focused. To achieve this I add a trigger that sets IsSelected to true when IsKeyboardFocusWithin is true:
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
When I add this trigger the Item is selected when the focus is on a child TextBox, but the first behaviour disappears. Now when I click outside the ListBox, the item is de-selected.
How can I keep both behaviours?
When your listbox looses focus, it will set selected item to null because of your trigger. You can select on focus using some code behind that will not unselect when you loose focus.
XAML:
<Window x:Class="SelectedTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<StackPanel>
<TextBox Text="Loose focus here" />
<ListBox Name="_listBox" ItemsSource="{Binding Path=Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" GotFocus="OnChildGotFocus">
<TextBox Text="{Binding .}" Margin="10" />
<TextBox Text="{Binding .}" Margin="10" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="Border" SnapsToDevicePixels="true" Background="Transparent">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</StackPanel>
</Window>
Code behind:
private void OnChildGotFocus(object sender, RoutedEventArgs e)
{
_listBox.SelectedItem = (sender as StackPanel).DataContext;
}
"When I add this trigger the Item is selected when the focus is on a child TextBox, but the first behaviour disappears. Now when I click outside the ListBox, the item is de-selected."
Actually, I don't think it has lost that original behavior. What I suspect is happening is you're clicking directly in the textbox from somewhere else so the underlying ListBoxItem never actually became selected. If it did however, you'd see the selection would still remain after you left as you want.
You can test this by forcing the ListBoxItem to be selected by clicking directly on it (side-note: you should always give it a background, even if just 'transparent' so it can receive mouse clicks, which it won't if it's null) or even just hitting 'Shift-Tab' to set the focus there, back from the textbox.
However, that doesn't solve your issue, which is that the TextBox gets the focus but doesn't let the underlying ListBoxItem know about it.
The two approaches you can use for that are an event trigger or an attached behavior.
The first is an event trigger on the IsKeyboardFocusWithinChanged event where you set 'IsSelected' to true if the keyboard focus changed to true. (Note: Sheridan's answer does a faux-change-notification but it should not be used in cases where you can multi-select in the list because everything becomes selected.) But even an event trigger causes issues because you lose the multi-select behaviors such as toggling or range-clicking, etc.
The other (and my preferred approach) is to write an attached behavior which you set on the ListBoxItem, either directly, or via a style if you prefer.
Here's the attached behavior. Note: You again would need to handle the multi-select stuff if you want to implement that. Also note that although I'm attaching the behavior to a ListBoxItem, inside I cast to UIElement. This way you can also use it in ComboBoxItem, TreeViewItem, etc. Basically any ContainerItem in a Selector-based control.
public class AutoSelectWhenAnyChildGetsFocus
{
public static readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached(
"Enabled",
typeof(bool),
typeof(AutoSelectWhenAnyChildGetsFocus),
new UIPropertyMetadata(false, Enabled_Changed));
public static bool GetEnabled(DependencyObject obj){ return (bool)obj.GetValue(EnabledProperty); }
public static void SetEnabled(DependencyObject obj, bool value){ obj.SetValue(EnabledProperty, value); }
private static void Enabled_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var attachEvents = (bool)e.NewValue;
var targetUiElement = (UIElement)sender;
if(attachEvents)
targetUiElement.IsKeyboardFocusWithinChanged += TargetUiElement_IsKeyboardFocusWithinChanged;
else
targetUiElement.IsKeyboardFocusWithinChanged -= TargetUiElement_IsKeyboardFocusWithinChanged;
}
static void TargetUiElement_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var targetUiElement = (UIElement)sender;
if(targetUiElement.IsKeyboardFocusWithin)
Selector.SetIsSelected(targetUiElement, true);
}
}
...and you simply add this as a property setter in your ListBoxItem's style
<Setter Property="behaviors:AutoSelectWhenAnyChildGetsFocus.Enabled" Value="True" />
This of course assumes you've imported an XML namespace called 'behaviors' that points to the namespace where the class is contained. You can put the class itself in a shared 'Helper' library, which is what we do. That way, everywhere we want it, its a simple property set in the XAML and the behavior takes care of everything else.
I figured out that IsKeyboardFocusWithin is not the best solution.
What I did in this case was to set the style on all of the controls used as DataTemplate to send the GotFocus-event to be handled in code behind. Then, in code behind, I searched up the visual tree (using VisualTreeHelper) to find the ListViewItem and set IsSelected to true. This way it does not "touch" the DataContext and works just with the View elements.
<Style TargetType="{x:Type Control}" x:Key="GridCellControlStyle">
...
<EventSetter Event="GotFocus" Handler="SelectListViewItemOnControlGotFocus"/>
...
private void SelectListViewItemOnControlGotFocus(object sender, RoutedEventArgs e)
{
var control = (Control)sender;
FocusParentListViewItem(control);
}
private void FocusParentListViewItem(Control control)
{
var listViewItem = FindVisualParent<ListViewItem>(control);
if (listViewItem != null)
listViewItem.IsSelected = true;
}
public static T FindVisualParent<T>(UIElement element) where T : UIElement
{
UIElement parent = element;
while (parent != null)
{
var correctlyTyped = parent as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
Right now you have to double click or click the + icon. Is there any way to make it so if a user clicks anywhere on the node it expands?
I had this same problem and found a good solution thanks to another StackOverflow post.
In the control.xaml's TreeView element, you can hook directly into the TreeViewItem's Selected event:
<TreeView ItemsSource="{StaticResource Array}" TreeViewItem.Selected="TreeViewItem_Selected"/>
Then in your control.xaml.cs code behind, you can grab that selected TreeViewItem from the RoutedEventArgs and set it to IsExpanded:
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem tvi = e.OriginalSource as TreeViewItem;
if (tvi == null || e.Handled) return;
tvi.IsExpanded = !tvi.IsExpanded;
e.Handled = true;
}
Nice and clean. Hopefully that helps someone!
Maybe is not the most elegant solution but this works:
static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
{
while (source != null && source.GetType() != typeof(T))
source = VisualTreeHelper.GetParent(source);
return source;
}
then in the TreeViewItem.Selected Handler:
private void Treeview_Selected(object sender, RoutedEventArgs e)
{
var treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
if (treeViewItem != null) treeViewItem.IsExpanded = true;
}
the VisualUpwardSearch magic is taken from here: Select TreeView Node on right click before displaying ContextMenu
Regards
I had the same problem and I did it with the Style functionnality so that you don't need to handle the event.
I defined a style for the TreeViewItem
<Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}">
<!--<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<CheckBox Style="{StaticResource TreeViewItemCB}" IsChecked="{Binding Path=IsExpanded,Mode=OneWayToSource,RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press">
<Grid Background="{StaticResource TreeViewItemBackground}" Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Name="Bd">
<ContentPresenter x:Name="PART_Header" ContentSource="Header"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.Row="1"/>
</Grid>
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The important part is to defined the checkBox in the ControlTemplate and the binding with it. When the CheckBox is checked, the item will be expanded with just one click.
<CheckBox Style="{StaticResource TreeViewItemCB}" IsChecked="{Binding Path=IsExpanded,Mode=OneWayToSource,RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press">
this is the style for the checkBox so that it stretches and doesn't display the box with the stroke.
<Style x:Key="TreeViewItemCB" TargetType="CheckBox" BasedOn="{StaticResource baseStyle}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
<Setter Property="Background" Value="Transparent"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<ContentPresenter VerticalAlignment="Stretch" HorizontalAlignment="Stretch" RecognizesAccessKey="True"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
#BJennings provided a great answer. However, if you want to expand or collapse an already selected item, it doesn't work. To improve that, you can simply add: tvi.IsSelected = false; (if you do not care whether the item is in a selected state.)
So, the whole codes look like this:
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem tvi = e.OriginalSource as TreeViewItem;
if (tvi == null || e.Handled) return;
tvi.IsExpanded = !tvi.IsExpanded;
tvi.IsSelected = false;
e.Handled = true;
}
Another approch would be to use Attached propperties.
public class VirtualOneClickExpandButtonBehavior : DependencyObject
{
public static bool GetEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(EnabledProperty);
}
public static void SetEnabled(DependencyObject obj, bool value)
{
obj.SetValue(EnabledProperty, value);
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(VirtualOneClickExpandButtonBehavior),
new UIPropertyMetadata(false, EnabledPropertyChangedCallback
));
private static void EnabledPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var treeView = dependencyObject as TreeView;
if (treeView == null) return;
treeView.MouseUp += TreeView_MouseUp;
}
private static void TreeView_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var treeViewItem = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
if (treeViewItem != null) treeViewItem.IsExpanded = !treeViewItem.IsExpanded;
}
static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
{
while (source != null && source.GetType() != typeof(T))
source = VisualTreeHelper.GetParent(source);
return source;
}
}
And then you can use it like this.
<TreeView controls:VirtualOneClickExpandButtonBehavior.Enabled="true" ItemsSource="{Binding HierarchicalModel}"/>
This is a good approch if you use the MVVM pattern because you don't need the codebehind.
And thaks to Markust for his VisualUpwardSearch(DependencyObject source)
The accepted solution has odd behaviour when you're navigating with the keyboard, and doesn't collapse the item when it's already selected. Alternatively, just derive a new class from TreeViewItem and override the MouseLeftButtonDown method. You also need to set your TreeView.ItemsSource to a collection of your new TreeViewItem class.
protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
if (!e.Handled && base.IsEnabled)
{
this.IsExpanded = !this.IsExpanded;
e.Handled = true;
}
base.OnMouseLeftButtonDown(e);
}
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Cursor" Value="Hand" />
<EventSetter Event="MouseUp" Handler="TreeViewItem_Click"/>
</Style>
</TreeView.ItemContainerStyle>
private void TreeViewItem_Click(object sender, MouseButtonEventArgs e)
{
((TreeViewItem) sender).IsExpanded = !((TreeViewItem) sender).IsExpanded;
Thread.Sleep(700);
}
Here is the answer, Enjoy it
Answer by: Ali Rahimy