As you can see in the image above, I have three columns in listbox.
First Column is FirstName, Second Column is LastName and Third Column is Age.
When I hit Enter on the Age Column a new Person is added to the list. Which eventually reflects the changes in ListBox.
Problem:
When I Press Enter on Age Column I get a new Person added as expected. But the focus does not go to the next ListItem. No matter how many times I press Enter I never get focus to the items added Programmatically.
Sample:
I have created a sample project that reproduces the issue:
Download Sample Project
I have a ListBox as follows:
<ListBox ItemsSource="{Binding People}" SelectedItem="{Binding SelectedPerson}">
<ListBox.Resources>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Focusable" Value="False" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="CurrentItemGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" Tag="IgnoreEnterKeyTraversal">
<TextBox.InputBindings>
<KeyBinding Command="{Binding DataContext.DeleteUnwantedOrderItemTransactionCommand,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
Gesture="Return" />
<KeyBinding Command="{Binding DataContext.DeleteUnwantedOrderItemTransactionCommand,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
Gesture="Tab" />
</TextBox.InputBindings>
</TextBox>
<TextBox Grid.Column="1" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" Margin="5,0"/>
<TextBox Grid.Column="2" Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}" Margin="5,0" Tag="IgnoreEnterKeyTraversal">
<TextBox.InputBindings>
<KeyBinding Command="{Binding DataContext.AddNewOrderItemTransactionCommand,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
Gesture="Return" />
<KeyBinding Command="{Binding DataContext.AddNewOrderItemTransactionCommand,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
Gesture="Tab" />
</TextBox.InputBindings>
</TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the ViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
IEventAggregator eventAggregator;
public MainWindowViewModel(IEventAggregator _eventAggregator)
{
People = new ObservableCollection<Person>();
People.Add(new Person());
eventAggregator = _eventAggregator;
DeleteUnwantedOrderItemTransactionCommand = new RelayCommand(DeleteUnwantedOrderItemTransaction);
AddNewOrderItemTransactionCommand = new RelayCommand(AddNewOrderItemTransaction);
}
public RelayCommand DeleteUnwantedOrderItemTransactionCommand { get; set; }
public RelayCommand AddNewOrderItemTransactionCommand { get; set; }
private ObservableCollection<Person> _People;
public ObservableCollection<Person> People
{
get
{
return _People;
}
set
{
if (_People != value)
{
_People = value;
OnPropertyChanged("People");
}
}
}
private Person _SelectedPerson;
public Person SelectedPerson
{
get
{
return _SelectedPerson;
}
set
{
if (_SelectedPerson != value)
{
_SelectedPerson = value;
OnPropertyChanged("SelectedPerson");
}
}
}
protected void DeleteUnwantedOrderItemTransaction(object obj)
{
if (SelectedPerson.FirstName == "")
{
People.Remove(SelectedPerson);
}
if (People.Count == 0)
{
People.Add(new Person());
}
eventAggregator.GetEvent<ChangeFocusToNextUIElementEvent>().Publish(true);
}
protected void AddNewOrderItemTransaction(object obj)
{
if (SelectedPerson == People.Last())
People.Add(new Person());
eventAggregator.GetEvent<ChangeFocusToNextUIElementEvent>().Publish(true);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
In Code-Behind:
public partial class MainWindow : Window
{
IEventAggregator _eventAggregator = new EventAggregator();
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel(_eventAggregator);
_eventAggregator.GetEvent<ChangeFocusToNextUIElementEvent>().Subscribe(MoveToNextUIElement);
}
void MoveToNextUIElement(bool obj)
{
// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
if (elementWithFocus != null)
{
elementWithFocus.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
The moment you fire your event you have added your element to your VM, but it might not have been bound and created by the View (ListBox) yet. Dispatching the focus request with a low priority might help. Also check if you can access the element by pressing TAB, so you know the traversal works.
elementWithFocus.Dispatcher.Invoke(() =>
elementWithFocus.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)),
DispatcherPriority.Input); // !
Virtualization of the ListBox Elements could also be an issue. You have to bring the added item into view.
cheeers.
Related
I have a TreeView, displaying information using TextBox, with the ability to edit.
But the TextBox stops the MouseDown event and the selection of the TreeViewItem does not occur when the TextBox is clicked.
How to solve this problem by leaving the ability to edit the text.
Smallest possible example: In this code, the SelectedItemChanged event is not fired because no item is selected.
<Window x:Class="TreeViewItemSelected.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<TextBlock x:Name="text" FontSize="20" HorizontalAlignment="Center" />
<TreeView x:Name="tree" FontSize="20" ItemsSource="{Binding Items}" SelectedItemChanged="tree_SelectedItemChanged">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBox Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
</Window>
using System.Collections.ObjectModel;
using System.Windows;
namespace TreeViewItemSelected
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new VM();
}
private void tree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue is testitem item) text.Text = item?.Name;
}
}
public class VM
{
public VM()
{
testitem item = new testitem("Aaaaaaa");
item.Items.Add(new testitem("Aaa 1"));
item.Items.Add(new testitem("Aaa 2"));
item.Items.Add(new testitem("Aaa 3"));
Items.Add(item);
item = new testitem("Bbbbbbb");
item.Items.Add(new testitem("Bbb 1"));
item.Items.Add(new testitem("Bbb 2"));
item.Items.Add(new testitem("Bdd 3"));
Items.Add(item);
Items.Add(new testitem("Ccccccc"));
Items.Add(new testitem("Ddddddd"));
Items.Add(new testitem("Eeeeeee"));
}
public ObservableCollection<testitem> Items { get; set; } = new ObservableCollection<testitem>();
}
public class testitem
{
public string Name { get; set; }
public testitem(string name) => Name = name;
public ObservableCollection<testitem> Items { get; set; } = new ObservableCollection<testitem> {};
}
}
I tried to use this, but it didn't work as I expected. Perhaps I am using it incorrectly.
AddHandler(UIElement.MouseDownEvent, new MouseButtonEventHandler(StackPanel_MouseDown), true);
or
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
Thanks in advance!
A straightforward way is to connect IsKeyboardFocused property of TextBox with IsSelected property of TreeViewItem. It can be done easily by Microsoft.Xaml.Behaviors.Wpf.
<TextBox Text="{Binding Name}">
<i:Interaction.Triggers>
<i:DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type TextBox}}, Path=IsKeyboardFocused}" Value="True">
<i:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
PropertyName="IsSelected" Value="True"/>
</i:DataTrigger>
</i:Interaction.Triggers>
</TextBox>
I want my ComboBox to be disabled when my collection is null or empty and to be enabled when I update the collection and fill it, in the same way as my "Connect" button.
I tried IsEnabled="{Binding CanConnect }", but it starts disabled and is not enabled when I fill the collection with the button uppdate.
I typed this with MVVM:
Model.cs
internal class PuertosCollection : ObservableCollection<Puerto>
{
}
internal class Puerto
{
public string Nombre { get; set; }
public SerialPort Valor { get; set; }
public override string ToString()
{
return Nombre;
}
}
ViewModel.cs
public bool CanConnect
{
get
{
return CurrentPuerto?.Valor.IsOpen != null;
}
}
private ICommand conectarCommand;
public ICommand ConectarCommand
{
get
{
if (conectarCommand == null)
conectarCommand = new RelayCommand(new Action(Conectar), () => CanConnect);
return conectarCommand;
}
}
private void Conectar()
{
currentPuerto.Valor.Open();
}
private Puerto currentPuerto;
public Puerto CurrentPuerto
{
get { return currentPuerto; }
set
{
currentPuerto = value;
RaisePropertyChanged("CurrentPuerto");
}
}
private PuertosCollection listaPuertos;
public PuertosCollection ListaPuertos
{
get { return listaPuertos; }
set
{
listaPuertos = value;
if (value != null && value.Count > 0)
{
CurrentPuerto = value[0];
}
RaisePropertyChanged("ListaPuertos");
}
}
private ICommand listarPuertosCommand;
public ICommand ListarPuertosCommand
{
get
{
if (listarPuertosCommand == null)
listarPuertosCommand = new RelayCommand(new Action(ListarPuertos));
return listarPuertosCommand;
}
}
private void ListarPuertos()
{
ListaPuertos = Generator.Puertos();
}
View.xaml
<Window.Resources>
<vm:ConfiguracionViewModel x:Key="ConfiguracionVM"/>
<vm:DatoViewModel x:Key="DatoVM"/>
</Window.Resources>
<DockPanel>
<StackPanel DataContext="{StaticResource ConfiguracionVM}" DockPanel.Dock="Top" Orientation="Horizontal" HorizontalAlignment="Left" Margin="8">
<Button Content="Conectar" Command="{Binding ConectarCommand}" Margin="0,0,8,0"/>
<Label Content="Puerto:" VerticalAlignment="Center"/>
<ComboBox ItemsSource="{Binding ListaPuertos}" SelectedItem="{Binding CurrentPuerto}" IsEnabled="{Binding CanConnect }" Width="Auto" VerticalContentAlignment="Center" Margin="0,0,8,0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ListarPuertosCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
<Button Command="{Binding ListarPuertosCommand}" VerticalAlignment="Center" Margin="0,0,8,0">
<StackPanel>
<Image Source="/Recursos/Imagenes/actualizar_96px.png" Width="32" Height="32" />
</StackPanel>
</Button>
<Button VerticalAlignment="Center">
<StackPanel>
<Image Source="/Recursos/Imagenes/ajustes_48px.png" Width="32" Height="32"/>
</StackPanel>
</Button>
</StackPanel>
<StatusBar DockPanel.Dock="Bottom">
<Label Content="Statusbar"/>
</StatusBar>
<DataGrid DataContext="{StaticResource DatoVM}">
</DataGrid>
</DockPanel>
I am learning MVVM with this tutorial
This is very simple.
First create a Converter that receives an integer that corresponds to the amount of items you have in the collection, if the amount is greater than zero it will return a True, if not it will return a False.
using System;
using System.Windows.Data;
namespace MyProject
{
public class CountToBoolean : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value is int) ? ((int)value) > 0 : false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Then you create an instance of the Converter in the App.xaml or in the window you want to use it.
<cv:CountToBoolean x:Key="CountToBoolean"/>
And finally you apply it to the ComboBox:
<ComboBox ItemsSource="{Binding ListaPuertos}" SelectedItem="{Binding CurrentPuerto}" IsEnabled="{Binding ListaPuertos.Count, Converter={StaticResource CountToBoolean}}" Width="Auto" VerticalContentAlignment="Center" Margin="0,0,8,0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ListarPuertosCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
That should do it.
I tried IsEnabled="{Binding CanConnect}", but it starts disabled and is not enabled when I fill the collection with the button uppdate.
At least from your provided code, you do not trigger the property changed event for the CanConnect property when it is changed.
Disabling the combo box
You can add a style to the ComboBox that disables it if the ListaPuertos collection is null or empty.
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ListaPuertos}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding ListaPuertos.Count}" Value="0">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
I cant update variable. I have a User control class with custom textbox.
WPF UC code.
<TextBox x:Name="wwwBox" TextChanged="WwwBoxTextChanged" Width="{Binding BoxWidth}" Text="{Binding LinkAddress,Mode=TwoWay}"/>
I was trying With Mode=TwoWay and without it.
WPF UC class code.
public partial class TextBoxWebControl : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int BoxWidth { get; set; }
public bool IsValid { get; set; }
public string LinkAddress { get; set; }
public TextBoxWebControl()
{
InitializeComponent();
DataContext = this;
IsValid = false;
}
public void WwwBoxTextChanged(object sender, TextChangedEventArgs args)
{
Uri uriResult;
IsValid = Uri.TryCreate(wwwBox.Text, UriKind.Absolute, out uriResult)
&& (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
PropertyChanged(this, new PropertyChangedEventArgs(nameof(IsValid)));
}
}
When i adding second PropertyChanged to link address i cant write anything to textbox. Next i need to send LinkAddres(content of textbox called wwwBox) via button. Here is a MainWindow WPF Code
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1">
<Label>Enter the website link..</Label>
</StackPanel>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Row="2">
<uc:TextBoxWebControl x:Name="wwwLink" BoxWidth="500" />
</StackPanel>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Row="3">
<Button Click="GetWebSiteDetail" Tag="{ Binding Path= LinkAddress, ElementName=wwwLink,UpdateSourceTrigger=PropertyChanged}" Content="Get all!" Width="100" Visibility="{Binding Path= IsValid, ElementName=wwwLink,UpdateSourceTrigger=PropertyChanged,Converter={StaticResource VisibleIfTrueConverter }}"/>
</StackPanel>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Row="4">
<Label Content="Bad address" >
<Label.Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path= IsValid,ElementName=wwwLink,UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path= IsValid,ElementName=wwwLink,UpdateSourceTrigger=PropertyChanged}" Value="False">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</StackPanel>
And Main window WPF class Code
private void GetWebSiteDetail(object sender, RoutedEventArgs e)
{
string link = new WebDetail(((Button)sender).Tag.ToString()); //here always null
}
Is my first wpf project. Glad for any advice how to solve my problem and how i can upgrade my code.
You need to raise the PropertyChanged event for the data bound LinkAddress property for the Tag property to get updated:
private string _linkAddress;
public string LinkAddress
{
get { return _linkAddress; }
set { _linkAddress = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(LinkAddress))); }
}
Also, there is no need to handle the TextChanged event for a data bound property. You might as well move your code from WwwBoxTextChanged to the setter of LinkAddress.
I have a custom ComboBox that have each item (Favorites and not favorites) is a Label + Button, then the last item have only a button to load all elements. Now I want to add a header as the first item, that says "Favorites".
Right now I have:
<ComboBox
x:Name="ComboBoxBtn"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="0,0,0,-1"
Width="300"
ItemsSource="{Binding Source, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedItem="{Binding Path=Selected, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"
IsSynchronizedWithCurrentItem="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Name="PART_GRID">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Content="{Binding}"
Width="250" Visibility="{Binding Path=., Converter={StaticResource elementToVisibilityConverter}}" />
<Button Name="PART_BUTTON"
Grid.Column="1"
Content="+"
Command="{Binding AddCommandButton, ElementName=root}"
CommandParameter="{Binding}"
Visibility="{Binding Path=., Converter={StaticResource elementToVisibilityConverter}}"/>
<Button Content="Carregar Todos" Margin="5,5"
Command="{Binding LoadAllCommandButton, ElementName=root}"
CommandParameter="{Binding ElementName=root, Path=FavoriteType}"
Visibility="{Binding Path=.,Converter={StaticResource elementToVisibilityForAddConverter}}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Favorite}"
Value="True">
<Setter TargetName="PART_GRID"
Property="Background"
Value="#FFE6E6FA" />
<Setter TargetName="PART_BUTTON"
Property="Content"
Value="-" />
<Setter TargetName="PART_BUTTON"
Property="Command"
Value="{Binding RemoveCommandButton, ElementName=root}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I preferred a different aproach, which i think is easier and more clean:
i created an empty interface IDrawable.
all classes i need to put inside the combobox should inherit from IDrawable
i created these:
MyLabel:
public class MyLabel : IDrawable
{
public string text { get; set; }
public MyLabel()
{
this.text = "MYTEXT";
}
}
MyButton:
public class MyButton : IDrawable
{
public string text { get; set; }
public MyButton()
{
this.text = "MYNBUTTON";
}
}
MyLabelButton:
public class MyLabelButton : IDrawable
{
public string labelText { get; set; }
public string buttonText { get; set; }
public MyLabelButton()
{
labelText = "labelText";
buttonText = "buttonText";
}
}
than here is the xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:WpfApplication1">
<Window.Resources>
<DataTemplate DataType="{x:Type me:MyButton}">
<Button Content="{Binding text}" />
</DataTemplate>
<DataTemplate DataType="{x:Type me:MyLabel}">
<TextBlock Text="{Binding text}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type me:MyLabelButton}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding labelText}"/>
<Button Content="{Binding buttonText}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid Name="MyGrid">
<ComboBox Name="MyCombo" ItemsSource="{Binding list}" SelectedItem="{Binding sel}" PreviewMouseLeftButtonUp="ComboBox_PreviewMouseLeftButtonUp"/>
</Grid>
</Window>
and codebehind:
public partial class MainWindow : Window
{
private IDrawable clicked;
public ObservableCollection<IDrawable> list { get; set; }
public IDrawable sel { get; set; }
public MainWindow()
{
InitializeComponent();
list = new ObservableCollection<IDrawable>();
list.Add(new MyLabel());
list.Add(new MyLabelButton());
list.Add(new MyButton());
this.DataContext = this;
}
private void ComboBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition(MyGrid);
clicked = null;
VisualTreeHelper.HitTest(
MyGrid,
null,
new HitTestResultCallback(ResultCallback),
new PointHitTestParameters(pt));
if (clicked != null)
{
((ComboBox)sender).IsDropDownOpen = false;
//do something
}
}
private HitTestResultBehavior ResultCallback(HitTestResult result)
{
DependencyObject parentObject = VisualTreeHelper.GetParent(result.VisualHit);
if (parentObject == null)
return HitTestResultBehavior.Continue;
var v = parentObject as Button;
if (v == null)
return HitTestResultBehavior.Continue;
if (v.DataContext != null && v.DataContext is IDrawable)
{
clicked = (IDrawable)v.DataContext;
return HitTestResultBehavior.Stop;
}
return HitTestResultBehavior.Continue;
}
}
the result is
as you can see I have standard combobox and custom elements. which i think it's better. in codebehind you can handle everything, like the impossibility of select the first label, call the command associated with the button, if the button is pressed, and so on.
In ComboBox_PreviewMouseLeftButtonUp i handled the click on the selected item, in case you want to do something particular if the selected button is pressed, and not show the dropdown menu.
this example is pretty barebones you need to customize it a little more and use MVVM everywhere.
at the moment you can push the button in the dropDown menu, you probabily want to disable the click if that buttont isn't selected.
EDIT
ComboBox_PreviewMouseLeftButtonUp should be like this:
private void ComboBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition((ComboBox)sender);
clicked = null;
VisualTreeHelper.HitTest(
(ComboBox)sender,
null,
new HitTestResultCallback(ResultCallback),
new PointHitTestParameters(pt));
if (clicked != null)
{
((ComboBox)sender).IsDropDownOpen = false;
//do something
}
}
(replaced Mygrid with (ComboBox)sender
I have Treeview which has also child nodes and is populated dynamically. The datacontext of the view is binded to view model.I want to get the selecteditem of the treeview when i double click any level of the treeview. I want to write EventToCommand for doubleclick event. I don't want to write any code in code behind.
The structure of the treeview is as follows. I have written InvokeCommandAction to pass doubleclick event to viewmodel using delegatecommand. This is working. But i am not getting the selecteditem when i double click on the child nodes. Always get root name.
<TreeView Name="treeView" Background="Transparent" ItemsSource="{Binding TreeList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<prism:InvokeCommandAction Command="{Binding TreeNodeDoubleClickevent}" CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeView}}}">
</prism:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected,Mode=TwoWay}" />
<Setter Property="IsExpanded" Value="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type UserControl}},Path=DataContext.IsExpanded, Mode=TwoWay}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="LightGray"/>
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type provider:dataprovider}" ItemsSource="{Binding providerList}">
<TextBlock Text="{Binding Name}" Background="{Binding Property.Background}" Tag="{Binding Parent}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
View Model code:
public class VM_TreeView : IVM_TreeView
{
public DelegateCommand<CustomXML> TreeNodeDoubleClickevent { get; set; }
[ImportingConstructor]
public VM_TreeView(IRegionManager regionManager)
: base(regionManager)
{
RegionManager = regionManager;
TreeNodeDoubleClickevent = new DelegateCommand<CustomXML>(OnExecute_TreeNodeDouble_click);
}
public void OnExecute_TreeNodeDouble_click(CustomXML obj)
{
}
}
Please help to achieve this.
It is working fine for me. Refer below code.
<Window x:Class="TreeView_Learning.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeView_Learning"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Interactivity;assembly=Microsoft.Practices.Prism.Interactivity"
Title="MainWindow" Height="650" Width="525">
<StackPanel>
<TreeView x:Name="tree" Width="500" Height="200" ItemsSource="{Binding NodeList}"
VerticalAlignment="Top" HorizontalAlignment="Center" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<prism:InvokeCommandAction Command="{Binding TreeNodeDoubleClickevent}" CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeView}}}">
</prism:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding NodeName}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
class ViewModel
{
private ObservableCollection<Node> myVar;
public ObservableCollection<Node> NodeList
{
get { return myVar; }
set { myVar = value; }
}
public DelegateCommand<Node> TreeNodeDoubleClickevent { get; set; }
public ViewModel()
{
NodeList = new ObservableCollection<Node>();
Node nd = new Node();
nd.NodeName = "Item 1.1";
Node nd1 = new Node();
nd1.NodeName = "Item 1.2";
Node nd2 = new Node();
nd2.NodeName = "Item 1";
nd2.Children.Add(nd);
nd2.Children.Add(nd1);
NodeList.Add(nd2);
TreeNodeDoubleClickevent = new DelegateCommand<Node>(MouseDoubleClick);
}
private void MouseDoubleClick(Node obj)
{
MessageBox.Show(obj.NodeName);
}
}
class Node
{
private string nodeName;
public string NodeName
{
get { return nodeName; }
set { nodeName = value; }
}
private ObservableCollection<Node> myVar = new ObservableCollection<Node>();
public ObservableCollection<Node> Children
{
get { return myVar; }
set { myVar = value; }
}
}