What event do I have to listen for, to get notified when a user selects an option from a (editable) WPF ComboBox control?
Do I have to access the Items property first to then listen to Items.CurrentChanged? And if so, how do I add that listener in XAML?
How about the SelectionChanged event?
EDIT: Added a simple example
<ComboBox SelectionChanged="ComboBox_SelectionChanged"/>
and in code-behind:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
if you are looking to do it in MVVM then its:
<ComboBox SelectedItem={Binding Path=SelectedItem}/>
assuming you have a SelectedItem property in your ViewModel set to the proper objectType.
Related
I have a WPF app where the content of a ListBox is updated when the user presses a button. My initial problem was refocusing the ListBox to a specific SelectedIndex value which is binded to an ActiveItem property in my ViewModel. I was able to solve this issue with the following code:
XAML:
<ListBox ItemsSource="{Binding ListOfItems}" SelectedIndex="{Binding ActiveItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsSynchronizedWithCurrentItem="True" SelectionChanged="ListBox_SelectionChanged" x:Name="ListBoxSelector">
Code-behind:
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBoxSelector.ScrollIntoView(ListBoxSelector.SelectedItem);
}
However, I currently have the above code-behind in the MainWindow.xaml.cs file instead of my ViewModel. My question is how do I move this code to the ViewModel so that I can stick to the MVVM pattern? I can't quite figure out how to properly address the ScrollIntoView property of the ListBox from the ViewModel.
You can force the selected item to scroll into view using a Behavior class.
public class perListBoxHelper : Behavior<ListBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
protected override void OnDetaching()
{
AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
base.OnDetaching();
}
private static void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBox = sender as ListBox;
if (listBox?.SelectedItem == null)
return;
Action action = () =>
{
listBox.UpdateLayout();
if (listBox.SelectedItem != null)
listBox.ScrollIntoView(listBox.SelectedItem);
};
listBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
}
}
More details on my blog post.
Also, personally I'd bind to SelectedItem of the ListBox rather than SelectedIndex, and handle any processing on item selection in that property setter, rather than using an event handler.
The MVVM pattern doesn't preclude the use of code behind. In fact, it can't because there are cases where code behind is the right thing to do. A view model is a model, it is not a view replacement. Another way to think about it is that the view model contains the what and the view contains the how. At least, this is my interpretation of the MVVM pattern.
In your case, the what are the list of items and the current item. The how is the ListBox itself and its default behavior. It seems to me that scrolling the selected item into view is an additional behavior, and therefore should be kept in the view. You're not violating the MVVM pattern because you're keeping the what in the view model and the how in the view.
I'm not sure if my solution is MVVM pattern.But to such problem,it can resolve the problem.
Here is what I will do: If Button is pressed ,it will trigger a command to call method in ViewModel.When ViewModel finish it's job, viewModel throw an custom event(where include item index where listbox should scroll into). And before this happen, when View is Loaded,View's Code-behind should listen to it's ViewModel through View's DataContext, and do scrollIntoView.
As I said,I'm not sure if it's MVVM way,But I thought it's acceptable.
There isn't the universal solution for every request regarding this and as others have mentioned MVVM doesn't mean that there is no code behind but no unnecessary code behind.
However in your particular request there is the solution if you want no code behind - make a class that inherits from ListView and handles request as you would like it to be handled and then use it in your XAML.
Sorry for misleading title, I'll try to explain better.
I've a TabControl like this:
<dragablz:TabablzControl SelectionChanged="MainTabs_SelectionChanged" x:Name="MainTabs">
where inside I've different TabItems, I need to fire the event MainTabs_SelectionChanged each time the user change the TabItem, this working but the event is fired also when the selection of a combobox, available inside the tabitem, change.
This is the ComboBox:
<ComboBox Grid.Column="1" Grid.Row="1" ItemsSource="{Binding Groups}"
Margin="8,0,8,16" DisplayMemberPath="Name" SelectedItem="{Binding SelectedGroup}" />
why happen this?
why happen this?
Because SelectionChanged is a routed event.
Routed Events Overview: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/routed-events-overview
You could use the OriginalSource property to determine whether a tab was selected:
private void MainTabs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.OriginalSource == MainTabs)
{
//do your thing
}
}
I have a form with two listboxes, listbox1 & listbox2. On form load I am filling up both the list boxes with same no. of items.
I want that if I select item at index 1 in listbox1 then in listbox2 item with the same index should also be selected.
How do I achieve this?
Subscribe on both listboxes SelectionChanged event and then set the SelectedIndex accordingly for the opposite listbox.
You can bind SelectedIndex in listBox2 to the SelectedIndex in listBox1.
Like so:
<ListBox Name="listBox1" />
<ListBox SelectedIndex="Binding ElementName=listBox1,Path=SelectedIndex" />
However, if you want to reflect the selection change on the listBox2 back to listBox1, you can't just do the same binding in listBox1, because it will throw StackOverflowException. You should subscribe to the SelectionChanged event on listBox2 and change the SelectedIndex of listBox1 in code.
Like so:
<ListBox Name="listBox2" SelectedIndex="Binding ElementName=listBox1,Path=SelectedIndex" SelectionChanged="listBox2_SelectionChanged" />
And the event handler method looks like this:
private void listBox2_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
listBox1.SelectedIndex = listBox2.SelectedIndex;
}
I'm working on a WPF application with MVVM pattern using Telerik controls.
Functionality:
I'm using telerik:RadListBox for which a collection is bind at runtime. I can ReOrder the items in the RadListBox.
Issue:
When i DragDrop items within RadListBox after DragLeave event the SelectionChanged event gets fired.
XAML:
<telerik:RadListBox x:Name="lstMarketSeries" ItemsSource="{Binding MarketSeriesCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True, ValidatesOnDataErrors=True}" ItemContainerStyle="{StaticResource DraggableListBoxItem}" DragLeave="lstMarketSeries_DragLeave" SelectionMode="Extended" telerik:StyleManager.Theme="Windows8" SelectionChanged="MarketSeriesCommit_SelectionChanged">
</telerik:RadListBox>
XAML.cs:
private void MarketSeriesCommit_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
private void lstMarketSeries_DragLeave(object sender, DragEventArgs e)
{
}
Is there any way that i can restrict the SelectionChanged event getting fired after DragLeave event?
I don't think that this is a good idea to prevent the selection event hadler to fire. But you can try to add the flag IsInDrag(boolean) and manage this during the darag/drop action(true on start dragging and false when you enter the final selection changed handler), in addition when you entering the SelectionChanged event handler and the flag is true you set this false and leave the handler(in addition you can set the handle property of the SelectionChangedEventArgs to true) that's all.
Regards.
I would like to know how to trigger the selectionChange event of a combobox only when the user himself change the selection of the list. (Avoid other cases) I found a solution here but I have some errors. Could you help me?
https://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.selectionchangecommitted(v=vs.110).aspx
I added System.Windows.Forms in my .cs file and it says there is an ambiguity beetween 'System.Windows.Controls.ComboBox' and 'System.Windows.Forms.ComboBox' with the first line of the code below.
I dont know how to cast my sender into a comboBox.
ComboBox senderComboBox = (ComboBox)sender;
if (senderComboBox.SelectionLength > 0)
{
//code here
}
Thanks for help!
You are trying to reference WinForms ComboBox and not WPF ComboBox. You can listen to SelectionChanged event of the control to know when user changes the selection.
A sample code from dotnetperls
XAML
<ComboBox
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"
Width="120"
Loaded="ComboBox_Loaded"
SelectionChanged="ComboBox_SelectionChanged"/>
CS File
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// ... Get the ComboBox.
var comboBox = sender as ComboBox;
// ... Set SelectedItem as Window Title.
string value = comboBox.SelectedItem as string;
this.Title = "Selected: " + value;
}
This sort of approach worked for me - XAML ComboBox SelectionChanged Fires OnLoad
Basically only wire-up the ComboBox_SelectionChanged event after the form has loaded - this will get you around the programmatic change that will fire onload. Beyond that I'm not sure. It might be that you need to use a different event, but I haven't looked into this.