I have a WPF, C# Application and acombobx like:
<ComboBox SelectedItem="{Binding MySelectedItem.MyString, ValidatesOnDataErrors=True}" ItemsSource="{Binding MyCollection}" />
Is there a way to bind to Selected, or let a command fire if a item is selected.
Background:
I want to start loading data based on the selected Item, the start triggger should be something like IsSelected or LostFocus
Any help or a different/better way of archive this would be very helpfully.
Thank you very much
There are couple of ways
1 - define your binded property as a full property and do the manipulations when the value changes:
private string myString;
public string MyString
{
get { return myString; }
set
{
myString = value;
// do your stuff here...
}
}
2 - Use interactivity and convert event to command:
<ComboBox ItemsSource="{Binding MyCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Don't fordet to add the needed namespace:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
And implement the SelectedItemChangedCommand in your ViewModel
Related
In my project i am using the autocompletebox of the WPF toolkit from dotnetprojects:
<input:AutoCompleteBox Grid.Row="0"
Height="30"
Width="300"
ItemsSource="{Binding Persons}"
SelectedItem="{Binding SelectedName, Mode=TwoWay}"
ValueMemberPath="DisplayName"
ItemTemplate="{StaticResource AutoCompleteBoxItemTemplate}"
ItemFilter="{Binding PersonFilter}"
Style="{DynamicResource AutoCompleteBoxStyle}"
x:Name="AutoCompleteBox">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding TextChanged}"
CommandParameter="{Binding ElementName=AutoCompleteBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</input:AutoCompleteBox>
In the event TextChanged i pass the AutoCompleteBox as parameter to the viewmodel:
private void TextChangedInternal(object obj)
{
var box = obj as AutoCompleteBox;
...
}
From this point on i have no idea how to access the suggestion listbox within the popup.
My intention is to highlight the entered query within the suggestions.
Has somebody an idea how to archive this?
You will need to change the item template to something you control. This means you set up an AutoCompleteBox.ItemTemplate. This item template will contain each result found.
This Tutorial is for silver light but is pretty much all you need to create your custom control to highlight the text in the results. Once you have that you add this in the data template of your ItemTemplate.
I've implemented MVVM in WPF and have a ListView as follows:
<ListView DockPanel.Dock="Top" Name="ListViewMain" ItemsSource="{Binding Items}">
... GridView in ListView.View ...
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding OnSelectionChangedCommand}"
CommandParameter="{Binding SelectedIndex,
ElementName=ListViewMain}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
Whenever I change selection by clicking on any item on the ListView, OnSelectionChangedCommand is called with correct SelectedIndex.
But when I change SelectedIndex programatically as follows:
ListViewMain.SelectedIndex = 0;
I get -1 in OnSelectionChangedCommand. How do I get correct SelectedIndex irrespective of selection change method?
Update
The answers in WPF Listview SelectionChanged event don't explain what'll happen when SelectedIndex is set programmatically and how to define the view model's property which is bound do SelectedIndex.
Finally I've got the solution. The ListView should bind SelectedIndex with a property in view model:
<ListView DockPanel.Dock="Top" Name="ListViewMain" ItemsSource="{Binding Items}"
SelectedIndex="{Binding SelectedIndex}">
The ListView shouldn't trigger on SelectionChanged event.
The ListView.DataContext.SelectedIndex (the view model's SelectedIndex) property should call SelectionChanged handler:
public int SelectedIndex
{
get => _selected_index;
set
{
SetProperty(ref _selected_index, value);
OnSelectionChangedCommand.Execute(_selected_index);
}
}
I have a simple WPF page with a custom control (BrowsingPanel) containing a ListBox, and another control (ItemDataSheet) which displays data related to the element which is selected in the ListBox. When I click on an item in the ListBox, a command is sent to the BrowsingPanelViewModel, which sends a message. The message is received by the ItemDataSheetViewModel, which updates the ItemDataSheet view.
This is my BrowsingPanel.xaml:
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
Background="DarkGray">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"
CommandParameter="{Binding ElementName=itemsList, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
</Grid>
It works well, except that I would like the first ListBox item to be selected by default. To do so, I've tried two things:
First, I've tried to select the first item in the BrowsingPanelViewModel's constructor as shown below.
public RelayCommand<MyItem> SelectedItemChangedCommand { get; private set; }
public BrowsingPanelViewModel()
{
SelectedItemChangedCommand = new RelayCommand<MyItem>(SelectedItemChanged);
MyItems = new ObservableCollection<MyItem>();
MyItems.Add(ParsetemFromResourceName("Resources/toto.txt"));
MyItems.Add(ParseItemFromResourceName("Resources/tata.txt"));
MyItems.Add(ParseItemFromResourceName("Resources/titi.txt"));
//Select the first item if there's one
if (MyItems.Any())
SelectedItemChanged(MyItems.First());
}
void SelectedItemChanged(MyItem selectedItem)
{
Messenger.Default.Send(new NotificationMessage<MyItem>(selectedItem, Notification.SelectedMyChanged));
}
This works fine, the ItemDataSheetViewModel displays the data corresponding to this item, but the item is not (visually) selected in the ListBox.
Then, I've tried to select the first item from the BrowsingPanel view. In the code behind, I have a handler for itemsList_Loaded which looks like this:
private void itemsList_Loaded(object sender, RoutedEventArgs e)
{
//Select the first item by default
itemsList.Focus();
if (itemsList.Items.Count > 0)
itemsList.SelectedItem = itemsList.Items[0];
}
And this is where I get a weird behavior. This selects the item correctly in the ListBox, but the SelectedItemChanged command is not triggered. And I don't understand why.
The funny part is that if I replace my EventTrigger with a SelectionChanged event that I put in the code behind as shown below, then the callback function is called.
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
Background="DarkGray"
Loaded="itemsList_Loaded"
SelectionChanged="itemsList_SelectionChanged"> <!-- This is called when changing SelectedItem in the Loaded -->
</ListBox>
</Grid>
Obviously, by combining the 2 solutions I have mentioned, it works: the bit in the view model constructor displays the appropriate data in the ItemDataSheet view, while the bit in the itemsList_Loaded visually selects the item in the List. But I don't find this very elegant...
It seems to me that programmatically changing the ListBox's SelectedIndex should trigger the SelectionChanged command, but it doesn't.
Any help will be appreciated!
Bare in mind this is a solution for a single select listbox.
You really don't need most of that code.
It can be as simple as only needing the SelectedValue property on the listbox:
<Grid>
<ListBox x:Name="itemsList"
ItemsSource="{Binding MyItems}"
SelectedValue="{Binding Path=MySelectedItem, Mode=TwoWay}"
Background="DarkGray">
</ListBox>
</Grid>
This can then be bound to your BrowsingPanelViewModel with the MySelectedItem property:
private MyItem m_MySelectedItem;
public MyItem MySelectedItem
{
get
{
return m_MySelectedItem;
}
set
{
m_MySelectedItem = value;
NotifyPropertyChanged("MySelectedItem");
}
}
The notifypropertychanged in the setter is key here.
You can then from your viewmodel select the first list item by assigning this property.
You will also need a DataTemplate for your MyItem object in the scope of your ListBox which can be as simple as:
<DataTemplate DataType={x:Type MyItem}>
<Textblock Text="{Binding Path=MyItemDescription"/>
</DataTemplate>
Or whatever.
I am trying to add an ItemsSource to a MenuItem while keeping the Command bound to my ViewModel (my Window's DataContext). So far, I haven't figured out a way to make it work. Before the ItemsSource is added, the binding is fine. The collection that I am trying to bind comes from a StaticResource. Can anybody help me out with this?
<MenuItem Command="{Binding OpenTeamPage}"
DisplayMemberPath="Name"
Header="Teams"
ItemsSource="{Binding Teams,
Source={StaticResource Container}}" />
I have tried using this and variations of it with no luck:
Command="{Binding OpenTeamPage,
RelativeSource={RelativeSource AncestorType=Window},
Mode=Default}"
If anybody could tell me how to use this ItemsSource while still binding my Command to my ViewModel, I would greatly appreciate it. I suppose I could put the Command in my Team model, but I would like to avoid that if possible.
EDIT : To clarify my problem, with the ItemsSource in place, the command in the ViewModel doesn't fire at all. Without the ItemsSource, the command fires. I would like to be able to have the ItemsSource and still be able to fire the command.
EDIT:
public class GameContainer
{
static GameContainer()
{
Teams = new ObservableCollection<Team>();
}
public static ObservableCollection<Team> Teams { get; set; }
}
In App.xaml:
<data:GameContainer x:Key="Container" />
The collection is populated when the program is started.
My goal once I get this working is to pass the selected team to the Viewmodel, hopefully via CommandParameter, and display info regarding the selected team.
EDIT: I was mistaken in my original post. A bound collection coming from the Viewmodel does not work either.
This is the behaviour of MenuItem, Item having Child MenuItem won't fire Command and it also should not as it does not make sense. But if you still want to fire a command on Parent Item click,there are two options
You can use Interactivity Triggers on your MenuItem to call command on MouseDown event like
<MenuItem
DisplayMemberPath="Name"
Header="Teams"
ItemsSource="{Binding Teams,
Source={StaticResource Container}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<cmd:EventToCommand Command="{Binding OpenTeamPage}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
you can define a Attached Property for command and define the MenuItem MouseDown behaviour like
public static class MouseCommandBehavior
{
public static readonly DependencyProperty MouseDownCommandProperty =
DependencyProperty.RegisterAttached("MouseDownCommand",
typeof(ICommand),
typeof(MouseCommandBehavior),
new FrameworkPropertyMetadata(null, (obj, e) => OnMouseCommandChanged(obj, (ICommand)e.NewValue, false)));
public static ICommand GetMouseDownCommand(DependencyObject d)
{
return (ICommand)d.GetValue(MouseDownCommandProperty);
}
public static void SetMouseDownCommand(DependencyObject d, ICommand value)
{
d.SetValue(MouseDownCommandProperty, value);
}
private static void OnMouseCommandChanged(DependencyObject d, ICommand command)
{
if (command == null) return;
var element = (FrameworkElement)d;
element.PreviewMouseDown += (obj, e) => command.Execute(null);
}
}
}
and you can set this Property value on your menuItem
<MenuItem local:MouseCommandBehavior.MouseDownCommand="{Binding OpenTeamPage}"
DisplayMemberPath="Name"
Header="Teams"
ItemsSource="{Binding Teams,
Source={StaticResource Container}}">
MenuItem will not execute its command if it's not a leaf node. Only menu items that are leafs (items with no children) are executing a command.
This is probably done due to convention - when you click an items that has children you get the children shown immediately, otherwise there's a delay from mouse hover till children shown.
Although it's probably a bad idea (from UX point of view) to have command on a parent, it's possible:
<MenuItem DisplayMemberPath="Name"
Header="{Binding OpenTeamPage}"
ItemsSource="{Binding Teams, Source={StaticResource Container}}" >
<MenuItem.HeaderTemplate>
<DataTemplate>
<!--Probably need to make this button transparent-->
<Button Content="Teams"
Command="{Binding }"/>
</DataTemplate>
</MenuItem.HeaderTemplate>
<!--This style is for the children to fire the same command as the parent-->
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command"
Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}, Path=Header}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
Depending upon your design, you'd might need to style the button to be transparent.
This is my xaml
<ListBox x:Name="HistoryList"
ItemsSource="{Binding HistoryCollection}"
>
<ListBox.ItemTemplate >
</DataTemplate>
<CheckBox x:Name="UpCheckBox" Height="50" Width="50" >
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="Checked">
<interactivity:InvokeCommandAction Command="{Binding UpCheckedCommad}" CommandParameter="{Binding ElementName=UpCheckBox}"></interactivity:InvokeCommandAction>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate >
</ListBox >
In ViewModel I have used GalasoftMVVM Command Binding
public ICommand UpCheckedCommad
{
get { return new RelayCommand<Object>(x => { PerformUpforTracks(x); }); }
}
void PerformUpforTracks(object x)
{
//TODO
}
I used a CheckBox inside a ListBox ItemTemplate.But am not getting the Checked Event of CheckBox in the ViewModel .
I wanted to get the Checked Event from my ViewModel.Can anyone have any idea to resolve this issue?
Each instance of your ListBox.ItemTemplate is automatically given "the current item in the collection" as its DataContext. In your case, that is each individual item in the HistoryCollection. In your example, the EventTrigger is searching for the "ThumbsUpCheckedCommad" inside your current instance of the HistoryItem.
In order to force the EventTrigger to search in your desired ViewModel, you need to specify the "Source" property of your command binding. I suggest using the RelativeSource syntax, to search up the tree for the last Control to have your ViewModel as its DataContext.
It would look something like this.
{Binding Path=ThumbsUpCheckedCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}
I got it By Binding Command by this way
Command="{Binding Path=DataContext.UpCheckedCommad,
ElementName=HistoryList}"