Prevent SelectionChanged event during DragDrop WPF - c#

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.

Related

SelectionChanged fired also on nested controls?

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
}
}

How to trigger a selectionChange event only when users change the selection of a combobox in C# wpf?

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.

Click Event not fired in WPF Datagrid in unselected row

I have a DataGrid (WPF 4) like this:
<DataGrid Margin="0,0,0,5" VerticalAlignment="Top" Height="192"
BorderBrush="#aaa" Background="White"
HorizontalAlignment="Left"
ItemsSource="{Binding Namen, Mode=OneWay}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
AutoGenerateColumns="False"
ColumnHeaderHeight="24"
SelectionChanged="DataGridAuslaendischeAlteNamen_SelectionChanged">
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="Namenseintrag" Binding="{Binding DisplayName, Mode=OneWay}" />
<DataGridTextColumn Width="75" Header="gültig von" Binding="{Binding GueltigAb, StringFormat=d, Mode=OneWay}" />
<DataGridTextColumn Width="75" Header="gültig bis" Binding="{Binding GueltigBis, StringFormat=d., Mode=OneWay}" />
<DataGridTemplateColumn Width="20" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Style="{DynamicResource CaratRemoveButton}"
Click="Button_Click" CommandParameter="{Binding}"
PreviewMouseDown="Button_PreviewMouseDown"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
The issue I experience is, that a button of the DataGridTemplateColumn does not fire the click-event, if its row is not selected. So I have to click a button twice, one time to select its row and then to raise the click event.
I read about similiar problems with checkbox-columns, but there the suggestion was obviously to use a template column.
I tested to use the PreviewMouseDown-Event of the button, which works, but is not what I want, since then the button does not follow its usual grpahical click behaviour.
What am I missing here? How can I get that click event by just clicking once, regardless of whether the row was selected or not?
basically, you have no solution except using a TemplateColumn and managing every single mouseEvent yourself.
Explanation :
click = mouseDown + MouseUp, right.
so your button needs to be able to get the mouseDown + the MouseUp event.
BUT...
by default, wpf's DataGrid has its rows handling the MouseDown Event so as to select the one you do the mouseDown on (to confirm: mouseDown on a cell and hold the mouseButton down, you'll see that the row is selected BEFORE you release the button).
So basically, the MouseDownEvent is Handled before it reaches the button, preventing you to be able to use the Click event on the button
Microsoft tell us in their doc that in such cases, we should turn to the Preview kind of event, but this cannot apply to click event since there is no way you could have a previewClickEvent
So the only solution I can see for you is to listen to both PreviewMouseDown and PreviewMouseUp on your button and simulating a click from them yourself
something a bit like this :
Button myButton = new Button();
bool mouseLeftButtonDownOnMyButton;
myButton.PreviewMouseLeftButtonDown += (s, e) => { mouseLeftButtonDownOnMyButton = true; };
myButton.PreviewMouseLeftButtonUp += (s, e) => {
if (mouseLeftButtonDownOnMyButton)
myButton.RaiseEvent( new RoutedEventArgs(Button.ClickEvent,myButton));
mouseLeftButtonDownOnMyButton = false;
};
myButton.Click += myButtonCLickHandler;
(of course, you'd need to translate that in your xaml template)
NB: this is not complete, you should also take care of the cases when the user does a mouseDown on the button but moves the mouse out of the button before doing the mouseup (in wich case you should reset the mouseLeftButtonDownOnMyButton flag). The best way would probably be to reset the flag in a general mouseUpEvent (on the window level for instance) instead of in the button's one.
Edit: the above code lets you manage the Click event as well and have only one code for both the real and simulated click events (hence the use of the RaiseEvent method), but if you don't need this, you could directly put your code in the PreviewMouseUp section as well of course.
Another solution would be to create your own button. One advantage of this: you do not have to hook up the events for every button.
public class FireOnPreviewButton : Button
{
#region Constructor
public FireOnPreviewButton()
{
PreviewMouseLeftButtonDown += OnLeftMouseButtonDownPreview;
}
#endregion
#region Event Handler
private void OnLeftMouseButtonDownPreview(object sender, MouseButtonEventArgs e)
{
// Prevent the event from going further
e.Handled = true;
// Invoke a click event on the button
var peer = new ButtonAutomationPeer(this);
var invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
if (invokeProv != null) invokeProv.Invoke();
}
#endregion
}
I realize this question is old but I'm on a 10 year contract that uses WPF and XAML and I ran into the same issue where users had to click a row in the datagrid twice to activate the mouse button down event. This is because the datagrid consumes the first click to select the row and the second click to trigger the event. I found a much simpler solution that works for my application:
PreviewMouseLeftButtonDown="MainDataGrid_OnMouseLeftButtonDown"
This triggers the mouse left button down event on the first click. This is how it looks in the datagrid setup:
<DataGrid Name="MainDataGrid" IsSynchronizedWithCurrentItem="True" SelectionMode="Extended" SelectionUnit="FullRow"
...removed other options for brevity
PreviewMouseLeftButtonDown="MainDataGrid_OnMouseLeftButtonDown"
...removed other options for brevity
RowHeight="30" BorderThickness="0" Background="#99F0F0F0" Grid.Column="{Binding GridColumn, Mode=TwoWay}" Grid.Row="1">
I used that instead of MouseLeftButtonDown. Cheers.
I had the same issue - I was using Image on Template Column and had event on MouseDown, but I had to click twice. So I called event handler on constructor and it worked for me.
You can try this:
constructor() {
datagridname.AddHandler(Button.ClickEvent, new RoutedEventHandler(Button_Click), true);
}
This may not be an option for you, but setting the to IsReadonly=true solved that issue for me.
It's too late but not for others.
I have been facing the same issue and spent almost 6 hours to find a workaround. I don't want someone else to waste their time again. After seeing all the possible answers. This is how I fixed it:
I bounded a method to the main/parent DataGrid's Loaded event. Method definition is:
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var grid = sender as DataGrid;
if (grid != null && grid.CurrentItem == null && grid.SelectedItem != null)
{
grid.CurrentItem = grid.SelectedItem;
}
else if (grid != null) //Fix when IsSynchronizedWithCurrentItem=True
{
var selectedItem = grid.SelectedItem;
grid.CurrentItem = null;
grid.SelectedItem = null;
grid.CurrentItem = selectedItem;
grid.SelectedItem = selectedItem;
}
}

How to make Silverlight TextBox.TextChanged event fire synchronously?

Platform: Silverlight 4 / .NET 4
Description:
I have a ComboBox, a Textbox and a NumericUpDown.
<StackPanel Margin="10">
<ComboBox SelectionChanged="cmbChanged" Margin="3">
<ComboBoxItem Content="A" />
<ComboBoxItem Content="B" />
</ComboBox>
<TextBox x:Name="txt" TextChanged="txtChanged" Margin="3"/>
<CheckBox x:Name="chk" Checked="chkChecked" Unchecked="chkChecked" Content="Check box" Margin="3"/>
<ListBox x:Name="lst" Height="100" Margin="3"/>
</StackPanel>
A list is for debugging purposes.
Repro:
Note that there are event handlers for TextBox.TextChanged, CheckBox.Checked, CheckBox.Unchecked and ComboBox.SelectionChanged.
Here are the handlers:
private void cmbChanged(object sender, SelectionChangedEventArgs e)
{
lst.Items.Clear();
txt.Text = (sender as ComboBox).SelectedIndex.ToString();
chk.IsChecked = !chk.IsChecked;
}
private void txtChanged(object sender, TextChangedEventArgs e)
{
lst.Items.Add("Text Changed");
}
private void chkChecked(object sender, RoutedEventArgs e)
{
bool? chk = (sender as CheckBox).IsChecked;
lst.Items.Add("CheckBox Changed to " + chk);
}
Problem:
In the combobox's event handler, I set the text in the textbox BEFORE setting the check state of the checkbox. However, if you take a look at the picture below, you will see that the event handler for the CheckBox.Checked gets called before TextBox.TextChanged.
The problem is obviously in the asynchronous execution of the TextBox.TextChanged event, as stated in the MSDN definition:
The TextChanged event is asynchronous. The event cannot be canceled.
I really need these event handlers to execute exactly in the order they are changed.
Question:
Is there any simple way to achieve what I need?
Perhaps it might be better to call your piece of business logic directly, instead of relying on the event handlers. You can always remove and add the EventHandler in case you don't want to hit that trigger in your particular situation:
//Remove event handlers:
CheckBox.Checked -= chkChecked;
TextBox.TextChanged -= txtChanged;
//Call your business stuff here
//You can also 'safely' set values on your textbox and
//checkbox since they wont trigger events now.
//Re-add event handlers, so they react on user input again:
CheckBox.Checked += chkChecked;
TextBox.TextChanged += txtChanged;
I'm not a Silverlight guy so these are suggestions:
Use the KeyDown/KeyUp events to capture changes to your text box.
Use the FocusManager to manage synchronous GotFocus/LostFocus events and check for changes there.

Event when ComboBoxItem in ComboBox gets selected

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.

Categories

Resources