How do I use the Key class in Silverlight - silverlight

I found the Key class for determining key input in a textbox but it seems to be just for wpf. How do I use it in Silverlight?
https://msdn.microsoft.com/en-us/library/aa274297(v=vs.60).aspx

I'm only aware of the Key enum in Silverlight; it contains the keys available on a keyboard so if you receive keyboard input you can determine which key was pressed and react accordingly. An examle:
public DropDownButton()
{
DefaultStyleKey = typeof( DropDownButton );
KeyDown += HandleEscapeKey;
}
private void HandleEscapeKey( object sender, KeyEventArgs e )
{
if (e.Key == Key.Escape && IsDropDownOpen)
{
CloseDropDown();
e.Handled = true;
}
}

Related

Is using an "Attached Property" a good practice for input capturing?

I am looking to find a generic way to support keyboard wedge scanning for my WPF TextBox controls.
(I am really a novice when it comes to more advanced WPF features, so I would like to ask if I am going in the right direction before I put a lot of time into research.)
What I am wanting to do is to add an Attached Property (or something) to my TextBoxes that will cause it to read all input into the box and then call a custom "ScanCompleted" command with the scanned input.
If an Attached Property is not a good fit for this, then is there a way to get this command on a TextBox without descending my own custom "ScanableTextBox"?
(Note: The criteria for a scan (instead of typed data) is that it will start with the Pause key (#19) and end with a Return key (#13).)
I think this could probably be accomplished with attached properties (behaviors), but would be much simpler and more straightforward to simply subclass TextBox and override the OnTextChanged, OnKeyDown, OnKeyUp and similar methods to add custom functionality.
Why don't you want to create your own control in this way?
update: Attached Behaviour
If you really don't want a derived control, here is an attached behaviour that accomplishes this (explanation below):
public class ScanReading
{
private static readonly IDictionary<TextBox, ScanInfo> TrackedTextBoxes = new Dictionary<TextBox, ScanInfo>();
public static readonly DependencyProperty ScanCompletedCommandProperty =
DependencyProperty.RegisterAttached("ScanCompletedCommand", typeof (ICommand), typeof (ScanReading),
new PropertyMetadata(default(ICommand), OnScanCompletedCommandChanged));
public static void SetScanCompletedCommand(TextBox textBox, ICommand value)
{
textBox.SetValue(ScanCompletedCommandProperty, value);
}
public static ICommand GetScanCompletedCommand(TextBox textBox)
{
return (ICommand) textBox.GetValue(ScanCompletedCommandProperty);
}
private static void OnScanCompletedCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBox = d as TextBox;
if (textBox == null)
return;
var command = (ICommand) e.NewValue;
if (command == null)
{
textBox.Unloaded -= OnTextBoxUnloaded;
textBox.KeyUp -= OnTextBoxKeyUp;
TrackedTextBoxes.Remove(textBox);
}
else
{
textBox.Unloaded += OnTextBoxUnloaded;
TrackedTextBoxes.Add(textBox, new ScanInfo(command));
textBox.KeyUp += OnTextBoxKeyUp;
}
}
static void OnTextBoxKeyUp(object sender, KeyEventArgs e)
{
var textBox = (TextBox) sender;
var scanInfo = TrackedTextBoxes[textBox];
if (scanInfo.IsTracking)
{
if (e.Key == Key.Return)
{
scanInfo.ScanCompletedCommand.Execute(textBox.Text);
scanInfo.IsTracking = false;
}
}
else if (string.IsNullOrEmpty(textBox.Text) && e.Key == Key.Pause)
{
TrackedTextBoxes[textBox].IsTracking = true;
}
}
static void OnTextBoxUnloaded(object sender, RoutedEventArgs e)
{
var textBox = (TextBox) sender;
textBox.KeyUp -= OnTextBoxKeyUp;
textBox.Unloaded -= OnTextBoxUnloaded;
TrackedTextBoxes.Remove(textBox);
}
}
public class ScanInfo
{
public ScanInfo(ICommand scanCompletedCommand)
{
ScanCompletedCommand = scanCompletedCommand;
}
public bool IsTracking { get; set; }
public ICommand ScanCompletedCommand { get; private set; }
}
Consume this by declaring a TextBox like so (where local is the namespace of your attached property, and ScanCompleted is an ICommand on your view-model):
<TextBox local:ScanReading.ScanCompletedCommand="{Binding ScanCompleted}" />
Now when this property is set, we add the TextBox to a static collection along with its associated ICommand.
Each time a key is pressed, we check whether it is the Pause key. If it is, and if the TextBox is empty, we set a flag to true to start looking for the Enter key.
Now each time a key is pressed, we check whether it is the Enter key. If it is, we execute the command, passing in the TextBox.Text value, and reset the flag to false for that TextBox.
We've also added a handler for the TextBox.Unloaded event to clean up our event subscriptions and remove the TextBox from the static list.

Determine whether Selector.SelectionChanged event was initiated by a user

Is it possible to determine whether a Selector.SelectionChanged event was initiated by the user or programmatically?
I.e. I need something like a boolean "IsUserInitiated" property that is true only if the SelectionChanged event was raised because the user changed the selection using mouse or keyboard.
Simple work around:
You could create a method that temporarily disables the SelectionChanged event and call it when you need to change the selection programmatically.
private void SelectGridRow( int SelectedIndex )
{
myDataGrid.SelectionChanged -= myDataGrid_SelectionChanged;
myDataGrid.SelectedIndex = SelectedIndex;
// other work ...
myDataGrid.SelectionChanged += myDataGrid_SelectionChanged;
}
This should work in most scenarios:
private void cboStatus_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.cboStatus.IsDropDownOpen)
{
//OPTIONAL:
//Causes the combobox selection changed to not be fired again if anything
//in the function below changes the selection (as in my weird case)
this.cboStatus.IsDropDownOpen = false;
//now put the code you want to fire when a user selects an option here
}
}
This is a problem I have had to work around since WinForms. I was hoping that in WPF they would add a boolean to SelectionChangedEventArgs called something like IsUserInitiated as mentioned in the question. I have most commonly needed this when I want to ignore anything happening while the data is loading and binding to the screen. For example, say I am defaulting a field based on the new value in SelectionChanged BUT I want the user to be able to overwrite this default value, and I only want the user to overwrite it, NOT the application when the screen reloads. I still feel like what I have been doing is hacky, but I will post it because I don't see it mentioned. No fancy tricks, just simple and effective.
1) Create a class level boolean called _loading
private bool _loading;
2) Update the boolean in the method doing the loading
private async Task Load()
{
_loading = true;
//load some stuff
_loading = false;
}
3) Use the boolean whenever you need to
private void SetDefaultValue(object sender, SelectionChangedEventArgs e)
{
if (!_loading) {
//set a default value
}
}
Taken from http://social.msdn.microsoft.com where the user post the same question
I don't think we can distinguish whether a SelectionChanged event was initiated by the user input or programmatically. SelectionChanged event doesn't care that.
Generally, you can always now whether it is initiated programmatically because it's your code that initiates it.
If you use DataBinding to bind the SelectedItem, you can set the NotifyOnSourceUpdated and NotifyOnTargetUpdated properties to True. And you can handle the Binding.SourceUpdated and Binding.TargetUpdated events. In most cases, the change initiated by the user inputs goes from Target to Source. If the change is initiated programmatically, it goes from Source to Target.
I don't know if it can help...
You could use an custom routed event and hook up the appropriate handlers in an behavior like this:
public class UserSelectionChangedEventArgs : RoutedEventArgs
{
public UserSelectionChangedEventArgs( RoutedEvent id, SelectionChangedEventArgs args , bool changedByUser) :base(id)
{
SelectionChangedByUser = changedByUser;
RemovedItems = args.RemovedItems;
AddedItems = args.AddedItems;
}
public bool SelectionChangedByUser { get; set; }
public IList RemovedItems { get; set; }
public IList AddedItems { get; set; }
}
public delegate void UserSelectionChangedEventHandler( object sender, UserSelectionChangedEventArgs e );
public class UserSelectionChangedBehavior : Behavior<Selector>
{
private bool m_expectingSelectionChanged;
public static readonly RoutedEvent UserSelectionChangedEvent = EventManager.RegisterRoutedEvent( "UserSelectionChanged", RoutingStrategy.Bubble, typeof( UserSelectionChangedEventHandler ), typeof( Selector ) );
public static void AddUserSelectionChangedHandler( DependencyObject d, UserSelectionChangedEventHandler handler )
{
( (Selector) d ).AddHandler( UserSelectionChangedEvent, handler );
}
public static void RemoveUserSelectionChangedHandler( DependencyObject d, UserSelectionChangedEventHandler handler )
{
( (Selector) d ).RemoveHandler( UserSelectionChangedEvent, handler );
}
private void RaiseUserSelectionChangedEvent( UserSelectionChangedEventArgs args )
{
AssociatedObject.RaiseEvent( args );
}
protected override void OnAttached()
{
AssociatedObject.PreviewKeyDown += OnKeyDown;
AssociatedObject.PreviewKeyUp += OnKeyUp;
AssociatedObject.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
AssociatedObject.PreviewMouseLeftButtonUp += OnMouseLeftButtonUp;
AssociatedObject.SelectionChanged += OnSelectionChanged;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.PreviewKeyDown -= OnKeyDown;
AssociatedObject.PreviewKeyUp -= OnKeyUp;
AssociatedObject.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDown;
AssociatedObject.PreviewMouseLeftButtonUp -= OnMouseLeftButtonUp;
AssociatedObject.SelectionChanged -= OnSelectionChanged;
base.OnDetaching();
}
private void OnMouseLeftButtonUp( object sender, MouseButtonEventArgs e )
{
m_expectingSelectionChanged = false;
}
private void OnKeyDown( object sender, KeyEventArgs e )
{
m_expectingSelectionChanged = true;
}
private void OnKeyUp( object sender, KeyEventArgs e )
{
m_expectingSelectionChanged = false;
}
private void OnMouseLeftButtonDown( object sender, MouseButtonEventArgs e )
{
m_expectingSelectionChanged = true;
}
private void OnSelectionChanged( object sender, SelectionChangedEventArgs e )
{
RaiseUserSelectionChangedEvent( new UserSelectionChangedEventArgs( UserSelectionChangedEvent, e, m_expectingSelectionChanged ) );
}
}
In XAML you could just subscribe to the UserSelectionChangedEvent like this:
<ListBox ItemsSource="{Binding Items}" b:UserSelectionChangedBehavior.UserSelectionChanged="OnUserSelectionChanged">
<i:Interaction.Behaviors>
<b:UserSelectionChangedBehavior/>
</i:Interaction.Behaviors>
Handler:
private void OnUserSelectionChanged( object sender, UserSelectionChangedEventArgs e )
{
if(e.SelectionChangedByUser)
{
Console.WriteLine( "Selection changed by user" );
}
else
{
Console.WriteLine( "Selection changed by code" );
}
}
This is just an idea. Probably you won't even need the behavior and just define the attached routed event. But then I have no idea where to store the m_expectingSelectionChanged flag. I also don't know if this works in all cases. But maybe it gives you a starting point.
Usually a Selector has it's selection set/changed when the control is loaded into view. When this happens the IsLoaded property is still false. When a user makes a selection manually the control obviously has to be visible and hence IsLoaded will be true. Try using this property to determine if a change is user initiated or due to the control being loaded.
Why do you want to know?
I have coded many dialogs where I had similar situations - I didn't really want to know that the user used the mouse or keyboard, but I did want a specific behaviour, and I did want effects from triggering some binding to behave the right way.
For most cases I have found that using the MVVM pattern - or at least separating logic from ui - you often avoid those problems.
So for your problem I would try to eliminate the selectionchanged handler and only use bindings - so your state of the gui is based on the model behind and not the wireing of events.
mvvm:
http://en.wikipedia.org/wiki/Model_View_ViewModel
You can check for AddedItems and RemovedItems. If it was initiated by user both properties has an item. If an item was just added via code the RemovedItems list should be empty. So
if (e.AddedItems.Count>0 && e.RemovedItems.Count > 0)
//Initiated by user

Default Text in silverlight search text box

I need a feature in silverlight text box, simillar functionality as in Ask Question "Title" textbox in stalkoverflow. When there's no text in textbox then it should display "Search". When user clicked on the textbox then textbox text should be empty and in textbox lost focus if the text is empty then show "Search". I wrote the following code, but is there any code which handles all possible conditions?
private void txtAvailable_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
txtAvailable.Text = "";
}
private void txtAvailable_LostFocus(object sender, RoutedEventArgs e)
{
if (txtAvailable.Text.Trim() == "")
txtAvailable.Text = "Search";
}
You could use the Textbox GotFocus and LostFocus events - they should be generic enough to cover off all of your potentials..
The specialness comes when you want to search on every keystroke - you have to enable and disable searching on those events.
private bool IsBusy
{
get;
set;
}
private bool CanSearch
{
get;
set;
}
public Constructor()
{
InitializeComponent();
this.IsBusy = false;
txtSearch.GotFocus += new RoutedEventHandler( txtSearch_GotFocus );
txtSearch.LostFocus += new RoutedEventHandler( txtSearch_LostFocus );
txtSearch.KeyUp += new System.Windows.Input.KeyEventHandler( txtSearch_KeyUp );
txtSearch.Text = "Search »";
}
private void txtSearch_LostFocus( object sender, RoutedEventArgs e )
{
if( string.IsNullOrEmpty( txtSearch.Text ) )
{
CanSearch = false;
txtSearch.Text = "Search »";
}
}
private void txtSearch_GotFocus( object sender, RoutedEventArgs e )
{
txtSearch.Text = string.Empty;
CanSearch = true;
}
private void OnFilterCommand()
{
try
{
if( !IsBusy && CanSearch )
{
AppMessages.FilterAssetMessage.Send( txtSearch.Text );
}
}
catch( Exception ex )
{
// Notify user if there is any error
AppMessages.RaiseErrorMessage.Send( ex );
}
}
private void txtSearch_KeyUp( object sender, System.Windows.Input.KeyEventArgs e )
{
OnFilterCommand();
}
If you're familiar with WPF and the differences between WPF and Silverlight, take a look at the WatermarkTextBox in the extended WPF toolkit:
http://wpftoolkit.codeplex.com/wikipage?title=WatermarkTextBox&referringTitle=Home
The source is available, so you could try porting that control to Silverlight.

In Silverlight Datagrid, How to add a new row when focus goes away from Last Row?

I want to add a new row in my Silverlight DataGrid, when user try to go from LastRow to NextRow by Tab/Enter (as it last row, DataGrid loses focus). I can not use RowEditEnded event as it will fire even if i move to a PreviousRow from LastRow.
Can anyone help me achieve this?
If you look at DataGrid source code you can see that it traps key down event (f.i. to realize functionality like go to next row on enter pressed). As solution I propose to implement own grid inherited from DataGrid and add event which raised when user presses enter(or other) button. Own control:
public class MyDataGrid : DataGrid
{
public event EventHandler OnLastRowEnterPressed;
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (ItemsSource != null
&& ItemsSource.Cast<object>().Count() - 1 == SelectedIndex
&& e.Key == Key.Enter)
{
RaiseLastRowEnterPressed();
}
}
private void RaiseLastRowEnterPressed()
{
if (OnLastRowEnterPressed != null)
OnLastRowEnterPressed(this, EventArgs.Empty);
}
}
Using:
ObservableCollection<Foo> source = new ObservableCollection<Foo>()
{
new Foo(), new Foo(), new Foo(),
};
myDataGrid.OnLastRowEnterPressed += (s, e) => source.Add(new Foo());
myDataGrid.ItemsSource = source;
well vladimir, there seems to be no simple/direct way to add new row after last row exit. your solution will work but with consequences of event being raised in other key press events also. i have come up with the combination of events to get the solution.
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
addFlag = (e.Key == Key.Tab);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
addFlag = (addFlag && true);
base.OnLostFocus(e);
}
protected override void OnRowEditEnded(DataGridRowEditEndedEventArgs e)
{
base.OnRowEditEnded(e);
addFlag = (addFlag && IsLastRowSelected);
if (addFlag)
AddItem();
addFlag = false;
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
addFlag = false;
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
addFlag = false;
}
private void AddItem()
{
if (RaiseAddEvent!= null)
{
this.Focus();
RaiseAddEvent(this, EventArgs.Empty);
this.UpdateLayout();
this.CurrentColumn = this.Columns[0];
this.BeginEdit();
}
}
You can use Routed events concept where trapping the Enter/Tab key , you can add new row to the data grid control.
I will expose by few steps. So lets start now..
1)Declare your event in constructor of this class.
this.DataGrid1.KeyDown += new KeyEventHandler(DataGrid1_KeyDown);
you also can it in XAML file.
...KeyDown="DataGrid1_KeyDown".....
2) Go to your keydown event & wrie the code.
var focusedElement = FocusManager.GetFocusedElement();
DataGrid detailsDataGrid = sender as DataGrid;
int dataGridrows = detailsDataGrid.ItemsSource.OfType<object>().Count();
if (e.Key == Key.Tab && (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
return;
if (e.Key == Key.Tab)
try
{
detailsDataGrid.SelectedIndex = row.GetIndex();
{
itemMaster.TransactionChilds.Add(transactionChild);
detailsDataGrid.SelectedItem = transactionChild;
}
}
3) Now code line by line..
DataGridRow row = DataGridRow.GetRowContainingElement(focusedElement as FrameworkElement);
DataGridColumn column = DataGridColumn.GetColumnContainingElement(focusedElement as FrameworkElement);
TransactionMaster itemMaster = this.DataFormVoucher.CurrentItem as TransactionMaster;
decimal serialNumber = 0;
if (buttonPress == "Modify")
if (dataGridrows - 1 == detailsDataGrid.SelectedIndex && column.DisplayIndex == 5)
TransactionChild transactionChild = new TransactionChild()"[None]",DateTime.Now.Date,catch (Exception ex)Console.WriteLine(ex.Message);
.DataGridChild.KeyDown += new KeyEventHandler(DataGridChild_KeyDown);
3) Now understand the code line by line
i) first 3 lines are used to take which row of a datagrid is selected.
ii)When new row will add in this case i have used Tab key you can also change this.Another things is if an user predd Tab+Shift then it will go through (default as control focus).
iii) then check is it last row & last column of this grid, if yes then add new row or else.
iv) to add a blank new row just pass your object (EDMX Model Table)

Keypad decimal separator on a Wpf TextBox, how to?

I have a Wpf application with some textbox for decimal input.
I would that when I press "dot" key (.) on numeric keypad of pc keyboard it send the correct decimal separator.
For example, on Italian language the decimal separator is "comma" (,)...Is possible set the "dot" key to send the "comma" character when pressed?
Quick and dirty:
private void NumericTextBox_KeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Decimal) {
var txb = sender as TextBox;
int caretPos=txb.CaretIndex;
txb.Text = txb.Text.Insert(txb.CaretIndex, System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);
txb.CaretIndex = caretPos + 1;
e.Handled = true;
}
}
Although you may set the default converter locale in WPF as suggested by Mamta Dalal it is not enough to convert the "decimal" key press to the correct string. This code will display the correct currency symbol and date/time format on data-bound controls
//Will set up correct string formats for data-bound controls,
// but will not replace numpad decimal key press
private void Application_Startup(object sender, StartupEventArgs e)
{
//Among other settings, this code may be used
CultureInfo ci = CultureInfo.CurrentUICulture;
try
{
//Override the default culture with something from app settings
ci = new CultureInfo([insert your preferred settings retrieval method here]);
}
catch { }
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
//Here is the important part for databinding default converters
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(ci.IetfLanguageTag)));
//Other initialization things
}
I found that handling the previewKeyDown event window-wide is a little cleaner than textbox-specific (it would be better if it could be applied application-wide).
public partial class MainWindow : Window
{
public MainWindow()
{
//Among other code
if (CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator != ".")
{
//Handler attach - will not be done if not needed
PreviewKeyDown += new KeyEventHandler(MainWindow_PreviewKeyDown);
}
}
void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Decimal)
{
e.Handled = true;
if (CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator.Length > 0)
{
Keyboard.FocusedElement.RaiseEvent(
new TextCompositionEventArgs(
InputManager.Current.PrimaryKeyboardDevice,
new TextComposition(InputManager.Current,
Keyboard.FocusedElement,
CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator)
) { RoutedEvent = TextCompositionManager.TextInputEvent});
}
}
}
}
If anybody could come up with a way to set it application-wide...

Resources