In Silverlight, a little box is drawn around the currently HIGHLIGHTED (NOT Selected) item when you press up or down arrow or tab. I want to find out which item the little box is around so I can make it the selected item when the user presses tab. I spent three days on this, maybe someone else can use it.
void SelectorRapidAccessKeyBehavior_DropDownOpened(object sender, EventArgs e)
{
FindPopup();
}
private void FindPopup()
{
CleanUpPopupHandler();
pop = GetPopup(base.AssociatedObject);
if (pop != null && pop.Child != null)
{
pop.Child.KeyDown += AssociatedObject_KeyUp;
foreach (FrameworkElement c in Finder.FindVisualChildren<FrameworkElement>(pop.Child))
{
c.KeyDown += new KeyEventHandler(c_KeyDown);
}
}
}
void c_KeyDown(object sender, KeyEventArgs e)
{
int t = this.AssociatedObject.TabIndex;
Border ci = sender as Border;
if (e.Key == Key.Tab)
{
if (ci != null)
{
//this here is the magic line
var v = Finder.FindVisualChildren<FrameworkElement>((DependencyObject)pop.Child).Where(a => a.Opacity > 0 && a.Name == "FocusVisualElement" && a.Visibility == Visibility.Visible);//&& )
object o = v.First().DataContext;
int i = this.AssociatedObject.Items.IndexOf(o);
if (i > -1)
this.AssociatedObject.SelectedIndex = i;
pop.IsOpen = false;
DependencyObject d = Finder.FindParent<FloatableWindow>(this.AssociatedObject);
if (d == null)
d = Finder.FindParent<Window>(this.AssociatedObject);
Control c = Finder.FindVisualChildren<Control>(d).Where(a => a.TabIndex > t).OrderBy(a => a.TabIndex).FirstOrDefault();
if (c == null)
c = Finder.FindVisualChildren<Control>(d).OrderBy(a => a.TabIndex).FirstOrDefault();
if (c != null)
c.Focus();
}
}
}
Just add a KeyDown event to the items (possibly easier said than done) and select the item if the Key is Tab, the event will be fired on the one that is focused (has the box around it), e.g.
<ComboBox Loaded="ComboBox_Loaded">
<ComboBoxItem>1</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
<ComboBoxItem>4</ComboBoxItem>
<ComboBoxItem>5</ComboBoxItem>
</ComboBox>
private void ComboBoxItem_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
var cbi = sender as ComboBoxItem;
var cb = cbi.Parent as ComboBox;
cb.SelectedItem = cbi;
e.Handled = true;
cb.IsDropDownOpen = false;
}
}
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
var cb = sender as ComboBox;
foreach (var item in cb.Items)
{
(item as ComboBoxItem).KeyDown += ComboBoxItem_KeyDown;
}
}
In WPF i would know of some cleaner ways to attach the events, maybe you can think of something.
void SelectorRapidAccessKeyBehavior_DropDownOpened(object sender, EventArgs e)
{
FindPopup();
}
private void FindPopup()
{
CleanUpPopupHandler();
pop = GetPopup(base.AssociatedObject);
if (pop != null && pop.Child != null)
{
pop.Child.KeyDown += AssociatedObject_KeyUp;
foreach (FrameworkElement c in Finder.FindVisualChildren<FrameworkElement>(pop.Child))
{
c.KeyDown += new KeyEventHandler(c_KeyDown);
}
}
}
void c_KeyDown(object sender, KeyEventArgs e)
{
int t = this.AssociatedObject.TabIndex;
Border ci = sender as Border;
if (e.Key == Key.Tab)
{
if (ci != null)
{
//this here is the magic line
var v = Finder.FindVisualChildren<FrameworkElement>((DependencyObject)pop.Child).Where(a => a.Opacity > 0 && a.Name == "FocusVisualElement" && a.Visibility == Visibility.Visible);//&& )
object o = v.First().DataContext;
int i = this.AssociatedObject.Items.IndexOf(o);
if (i > -1)
this.AssociatedObject.SelectedIndex = i;
pop.IsOpen = false;
DependencyObject d = Finder.FindParent<FloatableWindow>(this.AssociatedObject);
if (d == null)
d = Finder.FindParent<Window>(this.AssociatedObject);
Control c = Finder.FindVisualChildren<Control>(d).Where(a => a.TabIndex > t).OrderBy(a => a.TabIndex).FirstOrDefault();
if (c == null)
c = Finder.FindVisualChildren<Control>(d).OrderBy(a => a.TabIndex).FirstOrDefault();
if (c != null)
c.Focus();
}
}
}
Related
In C#, I cannot get DataGridViewCheckBoxColumn event to work. It always get a FALSE value even though I’ve clicked on the checkbox. The other datagridviews (text and combobox) columns work fine. Here is what I am doing…
OK, so I am dynamically creating datagridviews (DGVs) at runtime in my constructor based on how many tab sheets there are in the tab control which is determined by the number of weeks in any given date range i.e. one DGV per tab page (where you tab page for each week)
for (int i = 0; i < wcNumWeeks; i++)
{
foreach (DataRow dr in wbDatesDT.Rows)
{
if (Convert.ToInt16(dr["tabNo"].ToString()) == i + 1)
{
wcDate = Convert.ToDateTime(dr["wcDate"].ToString());
break;
}
}
weeksTabControl.TabPages.Add(wcDate.ToShortDateString());
weeksTabControl.TabPages[i].AutoScroll = true;
weeksTabControl.TabPages[i].Width = 1500;
weeksTabControl.TabPages[i].Height = 700;
weeksTabControl.TabPages[i].Controls.Add(new DataGridView()
{
Name = "dataGridView" + (i + 1).ToString(),
Dock = DockStyle.Fill,
Width = 1450,
Height = 650,
Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right),
ScrollBars = System.Windows.Forms.ScrollBars.Both,
AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
});
}
Again in the constructor, for each datagridview created I am creating event as follows:
foreach (Control thisControl in weeksTabControl.Controls)
{
if (thisControl.GetType() == typeof(TabPage))
{
foreach (Control dgv in thisControl.Controls)
{
if (dgv.GetType() == typeof(DataGridView))
{
BuildWhiteboardDGV((DataGridView)dgv);
PopulateWhiteboardDGV((DataGridView)dgv);
wbDataGridView = (DataGridView)dgv;
wbDataGridView.CellMouseUp += new DataGridViewCellMouseEventHandler(wbDataGridView_CellMouseUp);
wbDataGridView.CellEndEdit += new DataGridViewCellEventHandler(wbDataGridView_CellEndEdit);
wbDataGridView.CurrentCellDirtyStateChanged += new EventHandler(wbDataGridView_CurrentCellDirtyStateChanged);
wbDataGridView.CellValueChanged += new DataGridViewCellEventHandler(wbDataGridView_CellValueChanged);
}
}
}
}
The events themselves are as follows:
void wbDataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (wbDataGridView.IsCurrentCellDirty)
{
wbDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
void wbDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
try
{
if (e.ColumnIndex >= 13 && e.ColumnIndex <= 15)
{
System.Drawing.Point cur = new System.Drawing.Point(e.ColumnIndex, e.RowIndex);
DataGridViewCheckBoxCell curCell = (DataGridViewCheckBoxCell)wbDataGridView[cur.X, cur.Y];
if (curCell.Value != null && (bool)(curCell.Value) == true)
{
MessageBox.Show("TRUE");
}
else if (curCell.Value != null && (bool)(curCell.Value) == false)
{
MessageBox.Show("FALSE");
}
else
{
MessageBox.Show("NULL");
}
}
return;
}
catch (Exception ex )
{
MessageBox.Show("wbDataGridView_CellValueChanged() ERROR - " + ex.Message + " --> " + ex.InnerException.ToString());
return;
}
}
Where am I going wrong?
OK.... I think I've sorted it.
In my CellMouseUp() event, I had not catered for the LEFT button.
Therefore, by now adding that, the CellValueChanged() event works correctly and captures the correct DataGridViewCheckCellColumn check state : TRUE when checked and FALSE when unchecked.
public void wbDataGridView_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left && e.RowIndex != -1) // added this and it now works
{
this.rowIndex = e.RowIndex;
this.colIndex = e.ColumnIndex;
this.wbDataGridView = (DataGridView)sender;
return;
}
if (e.Button == System.Windows.Forms.MouseButtons.Right && e.RowIndex != -1)
{
this.rowIndex = e.RowIndex;
this.colIndex = e.ColumnIndex;
this.wbDataGridView = (DataGridView)sender;
return;
}
}
Thanks for your comments though. Much appreciated.
Jobs a good un !!!!
I want to check the mouse leave of tab area of TabPage. but its not working properly. sometimes I get the messagebox sometimes not.
TabPage mouseTab = null;
void tabControl1_MouseMove(object sender, MouseEventArgs e) {
TabPage checkTab = null;
for (int i = 0; i < tabControl1.TabPages.Count; ++i) {
if (tabControl1.GetTabRect(i).Contains(e.Location)) {
checkTab = tabControl1.TabPages[i];
}
}
if (checkTab == null && mouseTab != null) {
mouseTab = null;
MessageBox.Show("MouseLeave");
} else if (checkTab != null) {
if (mouseTab == null || !checkTab.Equals(mouseTab)) {
mouseTab = checkTab;
}
}
}
I have WPF application where in the Ui I have a header check box and when it is checked all the body checkboxes are checked. When any checkbox is checked, Im doing some work that I need by calling the Background worker which works perfect. If I select any checkboxes individually without checking header then Background worker works fine.
Problem is when I check the header all the body checkboxes are checked and in this case the Background worker 'Run Worker completed' event is being executed even before 'do work' is executed which is breaking my code. Please help. Here is my code. RunJob is from command for button in front end.
public void RunJob(object obj)
{
obforSave = obj;
workerforRun = new BackgroundWorker();
workerforRun.WorkerReportsProgress = true;
workerforRun.DoWork += workerforRun_DoWork;
workerforRun.ProgressChanged += workerforRun_ProgressChanged;
workerforRun.RunWorkerCompleted += workerforRun_RunWorkerCompleted;
if (!workerforRun.IsBusy)
{
workerforRun.RunWorkerAsync();
}
}
//Special case header is checked Runwroker completed is executing before do work.
void workerforRun_DoWork(object sender, DoWorkEventArgs e)
{
IsValidationsComplete = false;
int count = 0;
//var obj = e.Argument;
if (IsRunJobAlreadyExecuted == false)
{
ResultExtract = JobEntities.Any(s => s.ExtractIsSelected != null && (bool)s.ExtractIsSelected);
var resultTransform = JobEntities.Any(x => x.TransformIsSelected != null && (bool)x.TransformIsSelected);
var resultLoad = JobEntities.Any(b => b.LoadIsSelected != null && (bool)b.LoadIsSelected);
//Check if any of the entities are either Extracted, Transformed or Loaded.
if (ResultExtract || resultTransform || resultLoad)
{
SaveJobConfigurationChanges(obforSave);
var jobEngine = new JobEngine();
var jobId = JobEntities[0].JobId;
jobEngine.ProcessJob(jobId);
MessageBox.Show("Job Execution Complete", "Run Job");
AllResults = GetJobConfigurationResults();
foreach (var item in AllResults)
{
if (item.ExtractIsSelected == true && item.ExtractStatus == "Completed Successfully")
{
count += Convert.ToInt32(item.ExtractRowsSelected);
}
if (item.TransformIsSelected == true && item.TransformStatus == "Completed Successfully")
{
count += Convert.ToInt32(item.TransformRowsSelected);
}
if (item.LoadIsSelected == true && item.LoadStatus == "Completed Successfully")
{
count += Convert.ToInt32(item.LoadRowsSelected);
}
}
workerforRun.ReportProgress(count);
//MessageBox.Show(count.ToString());
}
else
{
//When No Entity is Selected and the Run Button is pressed.
MessageBox.Show("Select an Entity First");
}
}
IsRunJobAlreadyExecuted = false;
}
void workerforRun_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progress = e.ProgressPercentage;
}
void workerforRun_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
/*When Header is checked and when the Run is clicked, you want to Uncheck the header back after completion.
This property is set in the CS file.*/
AllResults = GetJobConfigurationResults();
foreach (var item in AllResults)
{
item.ExtractIsSelected = false;
item.TransformIsSelected = false;
item.LoadIsSelected = false;
}
SaveTaskSelection();
JobEntitiesCollectionViewSource.Source = AllResults;
JobEntitiesCollectionViewSource.View.Refresh();
ExecuteFilteredEntitiesStoredProcedure();
IsValidateEnabled = AllResults.Any(p => p.ExtractStatus == "Completed Successfully");
}), DispatcherPriority.Background, null);
MessageBox.Show(progress.ToString());
IsValidationsComplete = true;
}
CS file:
public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
// If there is a child found and if the child is of the T type.
//Dont remove null check . If no check i
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
private IEnumerable<CheckBox> GetAllCheckBoxs()
{
var allCheckBoxes = FindVisualChildren<CheckBox>(UserControlJob);
return allCheckBoxes;
}
private void ChkHeaderExtract_OnChecked(object sender, RoutedEventArgs e)
{
var extractCheckBoxes = GetAllCheckBoxs();
var i = 0;
foreach (var chk in extractCheckBoxes)
{
if (i%3 == 0) // Since I have many sets of checkboxes and I want only the first set of checkboxes checked when header is clicked Im doing this.
{
chk.IsChecked = true;
}
i++;
}
}
I am developing a winform application using c# I have successfully implemented a way to restrict textbox to two decimal places. How can I do it to one decimal place. ?
My code for two decimal places.\
private void txtHraRep_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsNumber(e.KeyChar) || e.KeyChar == '.')
{
if (Regex.IsMatch(
txtHraRep.Text,
"^\\d*\\.\\d{2}$")) e.Handled = true;
}
else e.Handled = e.KeyChar != (char)Keys.Back;
}
Changing to "^\d*\.\d{1}$")) e.Handled = true;
output
You can do this without regex by just checking where the decimal separator is in your text and then making sure that is 2 less than the length of the string (1 decimal place and 1 less for array length)
var decSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
var idx = txtBasic.Text.IndexOf(decSeparator);
if(idx + 2 >= txtBasic.Text.Length)
...
Instead of using a TextBox control for the input, look at using a MaskedTextbox control for your input. This will alleviate any self validation of the input and can show the users what input can be expected of them with messages as to why their input was not correct.
More information about the MaskedTextbox control:
MaskedTextbox
MaskedTextbox.Mask property
MSDN Walkthrough: Working with the MaskedTextBox Control
I just tried
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
{
if (char.IsNumber(e.KeyChar) || e.KeyChar == '.')
{
if (Regex.IsMatch(
textBox1.Text,
"^\\d*\\.\\d{1}$")) e.Handled = true;
}
else e.Handled = e.KeyChar != (char)Keys.Back;
}
}
and it worked as it should. It restricted the input to one digit after the decimal point.
But you could enter more than one decimal point and then also more digits.
So you can either try
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsNumber(e.KeyChar) || ((e.KeyChar == '.') && (textBox1.Text.IndexOf('.')== -1 )))
{
if (Regex.IsMatch(
textBox1.Text,
"^\\d*\\.\\d{1}$")) e.Handled = true;
}
else e.Handled = e.KeyChar != (char)Keys.Back;
}
or something like
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsNumber(e.KeyChar) || ((e.KeyChar == '.') && (textBox1.Text.IndexOf('.')== -1 )))
{
if (textBox1.Text.IndexOf('.') > 0)
{
if (textBox1.Text.IndexOf('.') < textBox1.Text.Length - 1)
e.Handled = true;
}
}
else e.Handled = e.KeyChar != (char)Keys.Back;
}
Create A new TextBox that inherit TextBox like
[DefaultBindingProperty("Text")]
[DefaultProperty("Text")]
[DefaultEvent("ValueChanged")]
public class SpecializedTextBox : TextBox
{
private bool _allowNegativeSign = false;
public bool AllowNegativeSign
{
get { return _allowNegativeSign; }
set { _allowNegativeSign = value; }
}
public decimal? DecimalValue
{
get
{
decimal k;
if (decimal.TryParse(this.Text, out k))
return k;
else
return null;
}
set
{
if (value.HasValue)
this.Text = value.Value.ToString();
else
this.Text = "";
}
}
private void This_TextChanged(object sender, EventArgs e)
{
string s = base.Text;
int cursorpos = base.SelectionStart;
bool separatorfound = false;
for (int i = 0; i < s.Length; )
{
if (char.IsNumber(s[i]))
i++;
else if (AllowNegativeSign && i < System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NegativeSign.Length && s.StartsWith(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NegativeSign))
i++;
else if (!separatorfound && s[i].ToString() == System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator)
{
separatorfound = true;
i++;
}
else
{
s = s.Remove(i, 1);
if (i < cursorpos)
cursorpos--;
}
}
if (base.Text != s)
{
base.Text = s;
base.SelectionStart = cursorpos;
base.SelectionLength = 0;
}
if (ValueChanged != null)
ValueChanged(this, EventArgs.Empty);
}
public event EventHandler ValueChanged;
private void InitializeComponent()
{
this.SuspendLayout();
//
// SpecializedTextBox
//
this.TextChanged += new System.EventHandler(this.This_TextChanged);
this.ResumeLayout(false);
}
public SpecializedTextBox()
: base()
{
InitializeComponent();
}
}
now use this text box and use DecimalValue to set or get your value
try:
List<string> doubleList = new List<string>(new string[]
{
"12345",
"1234.5",
"123.45",
"12.345",
"1.2345",
"1.2",
"1.23",
"1.234",
"1.23.45",
"12.3",
"123.4",
}) { };
private void button1_Click(object sender, EventArgs e)
{
foreach (var x in doubleList)
{
int countNumber = Regex.Matches(x, #"[0-9]").Count;
int countOfDot = Regex.Matches(x, #"\.").Count;
if (countOfDot == 1 && countNumber != 0) //contains "." and any digit
{
Console.WriteLine(x);
}
else if (countOfDot == 0 && countNumber != 0) //not contains "." and any digit
{
Console.WriteLine(x);
}
else
{
//do nothing . . .
}
}
}
output:
all except for **1.23.45** (2dots)
I want to have a Custom DataGrid which can,
Move to next cell when Enter key is pressed also if it is in edit mode.
When the last column in the current row is reach, the focus should move to the first cell of next row.
On reaching to next cell, if the cell is editable, it should automatically became editable.
If the cell contains an ComboBox not comboboxcolumn, the combobox should DropDownOpen.
Please help me in this. I have been trying from the past few day by creating a Custom DataGrid and wrote some code in
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
But I failed.
A much simpler implementation. The idea is to capture the keydown event and if the key is "Enter", then move to the next tab which is next cell of the grid.
/// <summary>
/// On Enter Key, it tabs to into next cell.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DataGrid_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
var uiElement = e.OriginalSource as UIElement;
if (e.Key == Key.Enter && uiElement != null)
{
e.Handled = true;
uiElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
private void dg_PreviewKeyDown(object sender, KeyEventArgs e)
{
try
{
if (e.Key == Key.Enter)
{
e.Handled = true;
var cell = GetCell(dgIssuance, dgIssuance.Items.Count - 1, 2);
if (cell != null)
{
cell.IsSelected = true;
cell.Focus();
dg.BeginEdit();
}
}
}
catch (Exception ex)
{
MessageBox(ex.Message, "Error", MessageType.Error);
}
}
public static DataGridCell GetCell(DataGrid dg, int row, int column)
{
var rowContainer = GetRow(dg, row);
if (rowContainer != null)
{
var presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
if (presenter != null)
{
// try to get the cell but it may possibly be virtualized
var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
// now try to bring into view and retreive the cell
dg.ScrollIntoView(rowContainer, dg.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
}
return null;
}
How about this solution? Cancel the action of the Enter key by setting Handled=true and press the Tab key.
public Constructor()
{
InitializeComponent();
this.SampleDataGrid.PreviewKeyDown += MoveCellOnEnterKey;
}
private void MoveCellOnEnterKey(object sender, KeyEventArgs e)
{
if(e.Key == Key.Enter)
{
// Cancel [Enter] key event.
e.Handled = true;
// Press [Tab] key programatically.
var tabKeyEvent = new KeyEventArgs(
e.KeyboardDevice, e.InputSource, e.Timestamp, Key.Tab);
tabKeyEvent.RoutedEvent = Keyboard.KeyDownEvent;
InputManager.Current.ProcessInput(tabKeyEvent);
}
}
public class DataGrid : System.Windows.Controls.DataGrid
{
private void PressKey(Key key)
{
KeyEventArgs args = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key);
args.RoutedEvent = Keyboard.KeyDownEvent;
InputManager.Current.ProcessInput(args);
}
protected override void OnCurrentCellChanged(EventArgs e)
{
if (this.CurrentCell.Column != null)
if (this.CurrentCell.Column.DisplayIndex == 2)
{
if (this.CurrentCell.Item.ToString() == "--End Of List--")
{
this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
}
}
else if (this.CurrentCell.Column != null && this.CurrentCell.Column.DisplayIndex == this.Columns.Count() - 1)
{
PressKey(Key.Return);
DataGridCell cell = DataGridHelper.GetCell(this.CurrentCell);
int index = DataGridHelper.GetRowIndex(cell);
DataGridRow dgrow = (DataGridRow)this.ItemContainerGenerator.ContainerFromItem(this.Items[index]);
dgrow.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
DataGridRow rowContainer = (DataGridRow)this.ItemContainerGenerator.ContainerFromItem(this.CurrentItem);
if (rowContainer != null)
{
int columnIndex = this.Columns.IndexOf(this.CurrentColumn);
DataGridCellsPresenter presenter = UIHelper.GetVisualChild<DataGridCellsPresenter>(rowContainer);
if (columnIndex == 0)
{
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
request.Wrapped = true;
cell.MoveFocus(request);
BeginEdit();
PressKey(Key.Down);
}
else
{
CommitEdit();
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
request.Wrapped = true;
cell.MoveFocus(request);
}
this.SelectedItem = this.CurrentItem;
e.Handled = true;
this.UpdateLayout();
}
}
}
}
For the time being, I have written this and its working for me.
Public Sub SendKey(ByVal key As Key)
Dim args As New KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key)
args.RoutedEvent = Keyboard.KeyDownEvent
InputManager.Current.ProcessInput(args)
End Sub
Private Sub dataGrid_PreviewKeyDown(sender As Object, e As KeyEventArgs) Handles dataGrid.PreviewKeyDown
Dim i As UIElement = e.OriginalSource
Dim DG As DataGrid = sender
If (e.Key = Key.Enter Or e.Key = Key.Return) AndAlso i IsNot Nothing Then
MyBase.OnKeyDown(e)
DG.CommitEdit()
SendKey(Key.Tab)
e.Handled = True
End If
End Sub
The key method is 'dataGrid.SetKeyboardFocusToCell'. So we can attach the KeyDown event:
private void dataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
DataGridTemplateColumn col = (DataGridTemplateColumn)dataGrid.CurrentColumn;
if (col != null)
{
switch (col.SortMemberPath)
{
case "From":
if (e.Key == Key.Enter && Keyboard.Modifiers == ModifierKeys.None) // Pure Enter
{
e.Handled = true;
int columnIndex = dataGrid.GetColumnIndex(colTo);
DataGridRow currentRow = dataGrid.GetRow(dataGrid.CurrentItem);
dataGrid.SetKeyboardFocusToCell(dataGrid.CurrentItem, columnIndex);
Dispatcher.Invoke(() =>
{
GridTimeSpanBox timeSpanBox = VisualTree.FindChild<GridTimeSpanBox>(currentRow, tsb => tsb.Name == "tsbTo", true);
timeSpanBox.SelectAll();
}, System.Windows.Threading.DispatcherPriority.ContextIdle);
}
break;
}
} // col != null
}
/// <summary>
/// Get the row container that holds the 'item'
/// </summary>
public DataGridRow GetRow(object item)
{
return (DataGridRow)ItemContainerGenerator.ContainerFromItem(item);
}
/// <summary>
/// Gets the index of a 'DataGridColum' or 'DataGridTemplateColumn' in the 'Columns' list. This doesn't change if the user
/// reorders the columns.
/// </summary>
public int GetColumnIndex(DataGridColumn column)
{
return this.Columns.IndexOf(column);
}
In this example the text in the following box is selected too. Can be useful for fields where most of the time the content is replaced by typing a new one.
Important to note that generally operations after 'dataGrid.SetKeyboardFocusToCell()' must be sent over the dispatcher to allow the UI to finish updating. Otherwise strange things can happen.
With this scheme you could for example even insert a row behind the current one.
private void dg_ExpenseItem_PreviewKeyDown(object sender, KeyEventArgs e)
{
try
{
DataGrid grid = (DataGrid)sender;
if (e.Key == Key.Enter)
{
e.Handled = true;
var cell = GetCell(grid, grid.SelectedIndex, grid.CurrentCell.Column.DisplayIndex+1);
if (cell != null)
{
cell.IsSelected = true;
cell.Focus();
grid.BeginEdit();
}
}
}
catch (Exception ex)
{
}
}
public static DataGridCell GetCell(DataGrid grid, int row, int column)
{
var rowContainer = GetRow(grid, row);
if (rowContainer != null)
{
var presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
if (presenter != null)
{
// try to get the cell but it may possibly be virtualized
var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
// now try to bring into view and retreive the cell
grid.ScrollIntoView(rowContainer, grid.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
}
return null;
}
static public DataGridRow GetRow(DataGrid dg, int index)
{
DataGridRow row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
if (row == null)
{
// may be virtualized, bring into view and try again
dg.ScrollIntoView(dg.Items[index]);
row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}