grid.Focus, TextBox and Delete key interaction? - wpf

I have the following code (a TextBox is focused by default):
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl))
{
if (Keyboard.IsKeyDown(Key.S))
{
grid.Focus();
ModifyPart(sender, e);
}
if (Keyboard.IsKeyDown(Key.Delete))
{
grid.Focus();
DeletePart(sender, e);
}
}
}
The Ctrl + S combination trigers, but Ctrl + Delete won't. Instead it deletes the contents of the TextBox(which I programmatically highlighted by default). I replaced Delete with Enter and it works, so I guess Delete key has some sort of a higher priority over everything else.
If I move grid.Focus(); out and above the if (Keyboard.IsKeyDown(Key.Delete)) method, then it works, but I lose functionalities of Ctrl+C, Ctrl+V, Ctrl+P, etc, because I'm removing focus from the TextBox.
Any idea how to overwrite the Delete priority?

Based on #ASh and #Soleil - Mathieu Prévot answers.
XAML:
PreviewKeyDown="Window_PreviewKeyDown">
CS:
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
this.Close();
}
if (Keyboard.IsKeyDown(Key.LeftCtrl))
{
if (Keyboard.IsKeyDown(Key.S))
{
ModifyPart(sender, e);
e.Handled = true;
}
if (Keyboard.IsKeyDown(Key.Delete))
{
DeletePart(sender, e);
e.Handled = true;
}
}
}

Related

Incorrect HitTest on resizing a DevExpress XtraGrid column

Custom sorting and column resize does not work.
Implemented custom sorting on MouseUp event handler however
if ( hitInfo.InColumnPanel && hitInfo.HitTest == GridHitTest.ColumnEdge)
{
(e as DXMouseEventArgs).Handled = true;
return;
}
does not work for the event.
Would like to be able to click on the column header to sort and resize by dragging column edge.
private void OnMouseDown(object sender, MouseEventArgs e)
{
GridHitInfo hitInfo = gridView1.CalcHitInfo(e.Location);
if (hitInfo.HitTest == GridHitTest.ColumnEdge)
{
isEdgeClicked = true;
}
}
private void OnMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (isEdgeClicked)
{ isEdgeClicked = false;
return;
}
else
{
(e as DXMouseEventArgs).Handled = true;
}
}

How can I select (with focus) the text in telerik:RadNumericUpDown

This NumericUpDown (NUD) floats over a map. When it gets visible I need to re-direct the next key-stroke inside the control overriding the current value.
With great pain I've found this solution:
private void LengthInput_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)(e.NewValue))
{
...
LengthInputBox.ShowButtons = true;
try
{
LengthInputBox.Focus();
if (m_lengthTextBox == null)
{
LengthInputBox.ApplyTemplate();
m_lengthTextBox = LengthInputBox.Template.FindName("textbox", LengthInputBox) as TextBox;
}
if (m_lengthTextBox != null)
{
m_lengthTextBox.SelectAll();
m_lengthTextBox.Focus();
}
}
finally
{
LengthInputBox.ShowButtons = false;
}
...
NUD is the LengthInputBox control. Focus method sets the focus on the NUD buttons.
Template.FindName("textbox"... retrieve the internal TextBox of NUD. If found, or previously found, it selects all and set focus on the text.
Finally, I remove the Up/Down buttons (I don't need them. Although I've done lot of variations with or without them, their presence does not change the behavior...)
It works for the first time, but on the second attempt it fails again.
Any ideas?
Select and Focus are bit slow. Using a Dispatcher has solved the issue:
private void LengthInputBox_GotFocus(object sender, RoutedEventArgs e)
{
if (m_lengthTextBox == null)
{
LengthInputBox.ApplyTemplate();
m_lengthTextBox = LengthInputBox.Template.FindName("textbox", LengthInputBox) as TextBox;
}
if (m_lengthTextBox != null)
{
m_lengthTextBox.Focusable = true;
m_lengthTextBox.IsTabStop = true;
if (!m_lengthTextBox.IsFocused)
Dispatcher.BeginInvoke(new Action(() =>
{
var dot = m_lengthTextBox.Text.IndexOf('.');
m_lengthTextBox.Select(dot, m_lengthTextBox.Text.Length - dot);
m_lengthTextBox.Focus();
}));
}
LengthInputBox.CaptureMouse();
}
(Don't forget to release the mouse:
private void LengthInput_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Escape:
case Key.Enter:
LengthInputBox.ReleaseMouseCapture();
ViewModel.IsLengthInputVisible = false;
e.Handled = true;
break;
}
}
)

What event is called when we click on a DataGridViewComboBoxCell

I have a list of item in a DataGridViewComboBoxCell; I want to do something as soon as the user click on a value.
What is the event, or how could I do what I want?
CellValueChanged is called when we leave the cell...
You can handle the selectededindexchanged of the combobox.
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
{
// first remove event handler to keep from attaching multiple:
cb.SelectedIndexChanged -= new
EventHandler(cb_SelectedIndexChanged);
// now attach the event handler
cb.SelectedIndexChanged += new
EventHandler(cb_SelectedIndexChanged);
}
}
void cb_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("Selected index changed");
}
Another possible way of doing it is:
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb == null)
return;
dataGridView1.EditingControlShowing -= dataGridView1_EditingControlShowing;
cb.SelectedIndexChanged += cb_SelectedIndexChanged;
}
void cb_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("Selected index changed");
}
You are doing everything just once.

What is the "pressed the delete key" event for the WPF Datagrid?

I want to enable the user to highlight a row on the WPF DataGrid and press delete key to delete the row.
the functionality is already built into the UI of the grid, so to the user, the row disappears
I currently handle this on the SelectionChanged event (code below)
I loop through all the "e.RemovedItems" and delete them with LINQ
Problem is: even when you simply select a row and move off of it, selection change is fired and that row is in e.RemovedItems (which is odd, why would simply selecting something put it in a RemovedItems container?).
So I am looking for a DeleteKeyPressed event so I can simply handle it. What is that event called?
I am using the March 2009 toolkit.
XAML:
<Grid DockPanel.Dock="Bottom">
<toolkit:DataGrid x:Name="TheDataGrid"
SelectionChanged="TheDataGrid_SelectionChanged"
AutoGenerateColumns="True"
RowEditEnding="TheDataGrid_RowEditEnding"/>
code-behind:
private void TheDataGrid_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count > 0)
{
Message.Text = "The following were removed: ";
foreach (object obj in e.RemovedItems)
{
Customer customer = obj as Customer;
Message.Text += customer.ContactName + ",";
_db.Order_Details.DeleteAllOnSubmit(
customer.Orders.SelectMany(o => o.Order_Details));
_db.Orders.DeleteAllOnSubmit(customer.Orders);
_db.Customers.DeleteOnSubmit(customer);
}
}
try
{
_db.SubmitChanges();
}
catch (Exception ex)
{
Message.Text = ex.Message;
}
}
ANSWER:
Thanks lnferis, that was exactly what I was looking for, here is my finished delete handling event for the datagrid, note the KeyDown event doesn't fire for some reason.
XAML:
<toolkit:DataGrid x:Name="TheDataGrid"
KeyDown="TheDataGrid_KeyDown"
PreviewKeyDown="TheDataGrid_PreviewKeyDown"
AutoGenerateColumns="True"
RowEditEnding="TheDataGrid_RowEditEnding"/>
code-behind
private void TheDataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
var grid = (DataGrid)sender;
if (grid.SelectedItems.Count > 0)
{
string checkMessage = "The following will be removed: ";
foreach (var row in grid.SelectedItems)
{
Customer customer = row as Customer;
checkMessage += customer.ContactName + ",";
}
checkMessage = Regex.Replace(checkMessage, ",$", "");
var result = MessageBox.Show(checkMessage, "Delete", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
foreach (var row in grid.SelectedItems)
{
Customer customer = row as Customer;
_db.Order_Details.DeleteAllOnSubmit(
customer.Orders.SelectMany(o => o.Order_Details));
_db.Orders.DeleteAllOnSubmit(customer.Orders);
_db.Customers.DeleteOnSubmit(customer);
}
_db.SubmitChanges();
}
else
{
foreach (var row in grid.SelectedItems)
{
Customer customer = row as Customer;
LoadData();
_db.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, customer); //TODO: this doesn't refresh the datagrid like the other instance in this code
}
}
}
}
}
private void TheDataGrid_KeyDown(object sender, KeyEventArgs e)
{
Console.WriteLine("never gets here for some reason");
}
The RemovedItems items reflects the items removed from the selection, and not from the grid.
Handle the PreviewKeyDown event, and use the SelectedItems property to delete the selected rows there:
private void PreviewKeyDownHandler(object sender, KeyEventArgs e) {
var grid = (DataGrid)sender;
if ( Key.Delete == e.Key ) {
foreach (var row in grid.SelectedItems) {
... // perform linq stuff to delete here
}
}
}
XAML
<DataGrid ItemsSource="{Binding}" CommandManager.PreviewCanExecute="Grid_PreviewCanExecute" />
Code behind
private void Grid_PreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
DataGrid grid = (DataGrid)sender;
if (e.Command == DataGrid.DeleteCommand)
{
if (MessageBox.Show(String.Format("Would you like to delete {0}", (grid.SelectedItem as Person).FirstName), "Confirm Delete", MessageBoxButton.OKCancel) != MessageBoxResult.OK)
e.Handled = true;
}
}
What are you binding your DataGrid to?
Ideally, you should react to CollectionChanged events on the collection you are binding to. That way, your logic (deletion of removed items) will be separated from your UI.
You can build an Observable collection containing your objects and bind it to ItemsSource just for that purpose if the original collection does not have the necessary events.
It might not suit your specific setup, but that's how I usually do it.
Please follow the below code. I have succeeded with the below code.
Please let me know if changes are required.
private void grdEmployee_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Device.Target.GetType().Name == "DataGridCell")
{
if (e.Key == Key.Delete)
{
MessageBoxResult res = MessageBox.Show("Are you sure want to delete?", "Confirmation!", MessageBoxButton.YesNo,MessageBoxImage.Question);
e.Handled = (res == MessageBoxResult.No);
}
}
}
A little late to the party, but to get Inferis answer working:
Dim isEditing = False
AddHandler dg.BeginningEdit, Sub() isEditing = True
AddHandler dg.RowEditEnding, Sub() isEditing = False
AddHandler dg.PreviewKeyDown, Sub(obj, ev)
If e.Key = Key.Delete AndAlso Not isEditing Then ...
This fixes epalms comment: "if you're editing a cell and use the delete key to remove some characters in the cell, you'll end up deleting the whole row"
The cleanest solution is to use PreviewCanExecute like answered by flux, this is a completed solution to make it a bit more clear for anybody that overlooked his answer like I did:
private void Grid_PreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (e.Command == DataGrid.DeleteCommand)
{
if (MessageBox.Show($"Delete something from something else?", "Confirm removal of something", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
// Do what ever needs to be done when someone deletes the row
}
else
{
e.Handled = true;
// Handled means.. no worries, I took care of it.. and it will not delete the row
}
}
}
No need to hook on to CommandManager.Executed after this.
You want to handle the KeyUp or KeyDown event and check the pressed Key for Delete.
private void OnKeyDown(object sender, KeyEventArgs e) {
if ( Key.Delete == e.Key ) {
// Delete pressed
}
}

Capturing tab in Silverlight TextBox

How can I capture a tab entered in a Silverlight TextBox and render 4 spaces (or a tab) in it's place?
I can't figure out how to block the tab navigation.
Here is what I do (similar to Johannes' code):
private const string Tab = " ";
void textBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
int selectionStart = textBox.SelectionStart;
textBox.Text = String.Format("{0}{1}{2}",
textBox.Text.Substring(0, textBox.SelectionStart),
Tab,
textBox.Text.Substring(textBox.SelectionStart + textBox.SelectionLength, (textBox.Text.Length) - (textBox.SelectionStart + textBox.SelectionLength))
);
e.Handled = true;
textBox.SelectionStart = selectionStart + Tab.Length;
}
}
This behaves just how you expect even if you select some text and hit the ol' "Tab" key.
One more thing: I tried having the tab string as "\t", but to no avail. The tab rendered, but was the width of a single space - hence the value for the Tab const being four spaces.
I am not sure how to solve your problem, I hacked together a solution though that seems to work.
Set the KeyDown event as below.
expenses.KeyDown += new KeyEventHandler(expenses_KeyDown);
In that event I put the following code:
void expenses_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
expenses.Text += " ";
expenses.Focus();
expenses.LostFocus += new RoutedEventHandler(expenses_LostFocus);
}
}
And then in LostFocus:
void expenses_LostFocus(object sender, RoutedEventArgs e)
{
expenses.Focus();
expenses.Select(expenses.Text.Length - 1, 0);
}
The last line in LostFocus sets the editing cursor to the end of the text, otherwise, when it gets focus, the cursor position is in the beginning of the textbox
This seemed to work well for me, and does not require the second event handler or hard coding of the text box name:
void TabbableTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
e.Handled = true;
var tb = ((TextBox)sender);
tb.Text += "\t";
tb.Select(tb.Text.Length, 0);
}
}

Resources