I am using a TextBox control for the user input in my Windows Phone 8.1 app.
How can I hide the characters as user gives input?
I am not using a PasswordBox because the defined InputScope is "Number" which is not possible in a PasswordBox.
While searching for a solution on the internet I found the only way by customizing the TextBox with the help of an UserControl.
Is there any easier way to do this without creating any UserControl?
Following is my code snippet:
In XAML page:
<TextBox Text="{Binding CardNo, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
MaxLength="17"
x:Name="CardNoTextBox"
InputScope="Number"
Margin="70,5"
PlaceholderText="Enter Your Card Number"
TextChanged="CardNoTextBox_TextChanged"
BorderBrush="Gray"
BorderThickness="2"
FontSize="20"/>
In code behind (xaml.cs):
private void CardNoTextBox_TextChanged(object sender, RoutedEventArgs routedEventArgs)
{
if (IsTextAllowed(CardNoTextBox.Text))
{
if (CardNoTextBox.Text.Length == 5)
{
if (CardNoTextBox.Text[4] != ' ')
{
string text = CardNoTextBox.Text.Insert(4, " ");
CardNoTextBox.Text = text;
CardNoTextBox.Select(CardNoTextBox.Text.Length, 0);
}
}
if (CardNoTextBox.Text.Length == 12)
{
if (CardNoTextBox.Text[11] != ' ')
{
string text = CardNoTextBox.Text.Insert(11, " ");
CardNoTextBox.Text = text;
CardNoTextBox.Select(CardNoTextBox.Text.Length, 0);
}
}
}
else
{
CardNoTextBox.Text = "";
}
}
After spending hours in finding an easier way I got an amazing solution. Hope this would help others too.
I simply added the following value to the FontFamily property of my TextBox control:
FontFamily="ms-appx:///Assets/PassDot.ttf#PassDot"
And gave the size of font 35,
FontSize="35"
This works just fine for my project.
I managed to create a custom TextBox, in which Text is *, but there is hiddenText that keeps the real string. Note that managing Caret position is not easy, because it changes due to some internal logic. Therefore, it is always at the end of the string. (Also note that you might need to handle some exceptions and bugs)
public class HiddenTextBox : TextBox
{
internal string hiddenText { get; private set; }
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Space)
addText(" ");
else if (e.Key == Key.Back)
removeText(true);
else if (e.Key == Key.Delete)
removeText(false);
else if (e.Key == Key.Return)
e.Handled = true;
base.OnPreviewKeyDown(e);
}
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
addText(e.Text);
e.Handled = true;
}
void addText(string text)
{
hiddenText = hiddenText != null ? hiddenText.Insert(CaretIndex, text) : text;
update();
}
void removeText(bool back)
{
if (hiddenText == null || hiddenText.Length == 0 || (back==false && CaretIndex == hiddenText.Length))
return;
if (back)
hiddenText = hiddenText.Substring(0, CaretIndex - 1) + hiddenText.Substring(CaretIndex, hiddenText.Length - CaretIndex);
else
hiddenText = hiddenText.Substring(0, CaretIndex) + hiddenText.Substring(CaretIndex+1, hiddenText.Length - CaretIndex);
update();
}
void update()
{
StringBuilder star = new StringBuilder();
foreach (var s in hiddenText)
{
star.Append("*");
}
Text = star.ToString();
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
if (hiddenText != null)
CaretIndex += hiddenText.Length;
}
}
Related
I want to search in a data grid via typing in a textbox, but I am unable to find solution.
Do I need to do any binding? If so, then how do I do it?
If you want filter text in your Datagrid i.e by Name, try this...
private bool DataMatchesFilterText(User user, string filterText)
{
return user.Name.ToString() == filterText;
}
Yeah you will require your data grid to be bound to a Property that contains all your data.
Then add a event handler to your Textbox to act on one of the key events, e.g.
Xaml:
<TextBox x:Name="SearchBox" KeyUp="FilterTextBox_TextChanged" />
Then in the code behind you need to act on that event. Here you need to extract the filter text, get the rows in your DataGrid and then perform some method to determine if it should be visible or not. You will need to implement your own DataMatchesFilterText method.
Codebehind:
private void FilterTextBox_TextChanged(object sender, KeyEventArgs e)
{
var filterTextBox = (TextBox)sender;
var filterText = filterTextBox.Text;
SetRowVisibilityByFilterText(filterText);
}
private void SetRowVisibilityByFilterText(string filterText)
{
GetVisibleRows(yourGrid)
.ToList()
.ForEach(
x =>
{
if (x == null) return;
x.Visibility =
DataMatchesFilterText(x.Item as YourRowProperty, filterText) ? Visibility.Visible : Visibility.Collapsed;
});
}
public static IEnumerable<DataGridRow> GetVisibleRows(DataGrid grid)
{
if (grid == null || grid.Items == null) yield break;
int count = grid.ItemsSource == null
? grid.Items.Count
: grid.ItemsSource.Cast<object>().Count();
for (int i = 0; i < count; i++)
{
yield return (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(i);
}
}
I have a datagrid view control. Now I have two textbox columns to it. Out of which the first I have set to Password. The problem is that whenever, I try to edit something in the other textbox, it is equally getting display in masked text's.How to avoid this?
My code is as under
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (dataGridView1.CurrentCell.ColumnIndex == 1)
{
TextBox tb = e.Control as TextBox;
if (tb != null)
{
tb.PasswordChar = '*';
}
}
}
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.ColumnIndex == 1 && e.Value != null)
{
e.Value = new string('*', e.Value.ToString().Length);
}
}
here is the solution:
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
TextBox tb = e.Control as TextBox;
if (tb != null)
{
if (dataGridView1.CurrentCell.ColumnIndex == 1)
{
tb.PasswordChar = '*';
}
else
{
tb.PasswordChar = (char)0;
}
}
}
the explanation will come shortly. i just can say for now that the tb.PasswordChar is '*' for all the textBox. still checking why
found the explanation here. basically "The DataGridView control hosts one editing control at a time" and therefor changing the PasswordChar property for one cell is editing it for the entire DataGrid.
That was very interesting studying this. thanks
As I'm new to the WPF development.
I want to know how to handle the Numeric Text Box.
As in old Windows Development Application i can handle the above mentioned scenario
in the Key Press Event but in WPF i won't have this Event.
So i have to handle this scenario in the Key Down Event.
But it was little Complicated can i know how to handle this one.
Condition :
Should Allow only Numeric and a single Decimal Point.
Should allow only 2 characters(Numbers) after the decimal point.
You have to create an extended textbox by inheriting from native TextBox. And override the OnTextInput method. Handle the input as you required. The following snippet will allow only numbers and not characters. Same way you can validate for your other needs.
protected override void OnTextInput(TextCompositionEventArgs e)
{
string text = e.Text.ToString();
double output = 0.0;
bool isnumber = Double.TryParse(text, out output);
if (!isnumber)
{
e.Handled = true;
}
base.OnTextInput(e);
}
you can use a behavior for this stuff
public class TextBoxInputBehavior : Behavior<TextBox>
{
const NumberStyles validNumberStyles = NumberStyles.AllowDecimalPoint |
NumberStyles.AllowThousands |
NumberStyles.AllowLeadingSign;
public TextBoxInputBehavior()
{
this.InputMode = TextBoxInputMode.None;
}
public TextBoxInputMode InputMode { get; set; }
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += AssociatedObjectPreviewTextInput;
AssociatedObject.PreviewKeyDown += AssociatedObjectPreviewKeyDown;
DataObject.AddPastingHandler(AssociatedObject, Pasting);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= AssociatedObjectPreviewTextInput;
AssociatedObject.PreviewKeyDown -= AssociatedObjectPreviewKeyDown;
DataObject.RemovePastingHandler(AssociatedObject, Pasting);
}
private void Pasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(string)))
{
var pastedText = (string)e.DataObject.GetData(typeof(string));
if (!this.IsValidInput(this.GetText(pastedText)))
{
System.Media.SystemSounds.Beep.Play();
e.CancelCommand();
}
}
else
{
System.Media.SystemSounds.Beep.Play();
e.CancelCommand();
}
}
private void AssociatedObjectPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
if (!this.IsValidInput(this.GetText(" ")))
{
System.Media.SystemSounds.Beep.Play();
e.Handled = true;
}
}
}
private void AssociatedObjectPreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (!this.IsValidInput(this.GetText(e.Text)))
{
System.Media.SystemSounds.Beep.Play();
e.Handled = true;
}
}
private string GetText(string input)
{
var txt = this.AssociatedObject;
var realtext = txt.Text.Remove(txt.SelectionStart, txt.SelectionLength);
var newtext = realtext.Insert(txt.CaretIndex, input);
return newtext;
}
private bool IsValidInput(string input)
{
switch (InputMode)
{
case TextBoxInputMode.None:
return true;
case TextBoxInputMode.DigitInput:
return CheckIsDigit(input);
case TextBoxInputMode.DecimalInput:
//minus einmal am anfang zulässig
if (input.Contains("-"))
if (input.IndexOf("-") == 0 && input.LastIndexOf("-")==0)
return true;
else
return false;
//wen mehr als ein Komma
if (input.ToCharArray().Where(x => x == ',').Count() > 1)
return false;
decimal d;
return decimal.TryParse(input,validNumberStyles,CultureInfo.CurrentCulture, out d);
default: throw new ArgumentException("Unknown TextBoxInputMode");
}
return true;
}
private bool CheckIsDigit(string wert)
{
return wert.ToCharArray().All(Char.IsDigit);
}
}
public enum TextBoxInputMode
{
None,
DecimalInput,
DigitInput
}
xaml
<TextBox Text="{Binding MyDecimalProperty}">
<i:Interaction.Behaviors>
<Behaviors:TextBoxInputBehavior InputMode="DecimalInput"/>
</i:Interaction.Behaviors>
</TextBox>
//It's not satisfy second condition
TextBox txtDecimal = sender as TextBox;
if ((e.Key == Key.OemPeriod || e.Key == Key.Decimal) && (txtDecimal.Text.Contains(".") == false))
{ e.Handled = false;}
else if ((e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) || (e.Key == Key.Back))
{ e.Handled = false; }
else if((e.Key >= Key.D0 && e.Key <= Key.D9))
{ e.Handled = false; }
else
{ e.Handled = true; }
Is there a way I can bind a Command to Ctrl+MWheelUp/Down? U know in a browser, you can do the same to increase/decrease font size? I want to replicate that effect in WPF. Possible? I was looking at InputBinding > MouseBindings and MouseAction does not seem to support Mouse Scrolls.
* I seem to have posted a similar question, but can't find it anymore
It can be done using very simple custom MouseGesture:
public enum MouseWheelDirection { Up, Down}
public class MouseWheelGesture : MouseGesture
{
public MouseWheelDirection Direction { get; set; }
public MouseWheelGesture(ModifierKeys keys, MouseWheelDirection direction)
: base(MouseAction.WheelClick, keys)
{
Direction = direction;
}
public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
{
var args = inputEventArgs as MouseWheelEventArgs;
if (args == null)
return false;
if (!base.Matches(targetElement, inputEventArgs))
return false;
if (Direction == MouseWheelDirection.Up && args.Delta > 0
|| Direction == MouseWheelDirection.Down && args.Delta < 0)
{
inputEventArgs.Handled = true;
return true;
}
return false;
}
}
public class MouseWheel : MarkupExtension
{
public MouseWheelDirection Direction { get; set; }
public ModifierKeys Keys { get; set; }
public MouseWheel()
{
Keys = ModifierKeys.None;
Direction = MouseWheelDirection.Down;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new MouseWheelGesture(Keys, Direction);
}
}
in the xaml:
<MouseBinding Gesture="{local:MouseWheel Direction=Down, Keys=Control}" Command="..." />
Ok, I did something like this in my ShellView : Window
this.KeyDown += (s, e) =>
{
_leftCtrlPressed = (e.Key == Key.LeftCtrl) ? true : false;
};
this.MouseWheel += (s, e) =>
{
if (_leftCtrlPressed) {
if (e.Delta > 0)
_vm.Options.FontSize += 1;
else if (e.Delta < 0)
_vm.Options.FontSize -= 1;
}
};
I think the Behaviour method will make things cleaner and more reusable, but I didn't really get it. It'll will be great if someone explained it in a simple way here?
Window has the MouseWheel event. You can do some command binding magic which you can then bind to a DataContext property. Check out this SO article for hints: Key press inside of textbox MVVM. Also take a look at this article: http://code.msdn.microsoft.com/eventbehaviourfactor
I simply bind the command using Interaction.Triggers.
You'll need to reference the expression interactivity namespace in XAML.
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseWheel">
<cmd:InvokeCommandAction Command="{Binding MouseWheelCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Then in the associated command.
private void MouseWheelCommandExecute(MouseWheelEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
if (e.Delta > 0)
{
if (Properties.Settings.Default.ZoomLevel < 4)
Properties.Settings.Default.ZoomLevel += .1;
}
else if (e.Delta < 0)
{
if (Properties.Settings.Default.ZoomLevel > 1)
Properties.Settings.Default.ZoomLevel -= .1;
}
}
}
If Delta is rising the mouse is scrolling Up, falling it is scrolling Down. I use this in an application where Scrolling will occur in scroll-able content but when either of the Ctrl keys are down, the application actually zooms.
I'm trying to figure out how to cancel user input in a TextBox when a validation error occurs. If the user attempts to enter an invalid character I would like to prevent it from being added to the TextBox.
How can I add to or modify the code below to prevent the TextBox from accepting invalid characters? Is it possible without listening to the TextBox.TextChanged event?
My TextBox looks like:
<TextBox Validation.Error="OnSomeTextBoxValidationError">
<TextBox.Text>
<Binding Path="Value" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:SomeValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
My custom validation rule looks like:
public class SomeValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
string hex_string = value as string;
Match invalid_chars = Regex.Match(hex_string, "[^0-9a-fA-F]");
bool is_valid = (invalid_chars.Success == false);
string error_context = null;
if (is_valid == false)
{
error_context = "Invalid characters";
}
return new ValidationResult(is_valid, error_context);
}
}
I have an error handler... can I do anything with it?
private void OnSomeTextBoxValidationError(object sender, ValidationErrorEventArgs e)
{
// Can I do anything here?
}
Please provide an original answer if possible, rather than referring to a URL. I've read a lot of possible solutions involving event handlers, but I haven't come across anyone discussing the possibility of doing all my validation in the ValidationRule.
After a lot of research it seems that the only way to have full control over the input to a TextBox is to handle several events directly. According to WPF Recipes in C# 2008 (1st ed., p. 169):
Unfortunately, there's no easy way (at present) to combine the useful, high-level data binding feature with the lower-level keyboard handling that would be necessary to prevent the user from typing invalid characters altogether.
Here's what I came up with to create a hexadecimal numeric TextBox which only accepts characters a-f, A-F and 0-9.
SomeClass.xaml
<TextBox
x:Name="SomeTextBox"
LostFocus="TextBoxLostFocus"
PreviewKeyDown="TextBoxPreviewKeyDown"
PreviewTextInput="TextBoxPreviewTextInput" />
SomeClass.xaml.cs
private string mInvalidCharPattern = "[^0-9a-fA-F]";
// In my case SomeClass derives from UserControl
public SomeClass()
{
DataObject.AddPastingHandler(
this.SomeTextBox,
new DataObjectPastingEventHandler(TextBoxPasting));
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
// You may want to refresh the TextBox's Text here. If the user deletes
// the contents of the TextBox and clicks off of it, then you can restore
// the original value.
}
// Catch the space character, since it doesn't trigger PreviewTextInput
private void TextBoxPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space) { e.Handled = true; }
}
// Do most validation here
private void TextBoxPreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (ValidateTextInput(e.Text) == false) { e.Handled = true; }
}
// Prevent pasting invalid characters
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
string lPastingText = e.DataObject.GetData(DataFormats.Text) as string;
if (ValidateTextInput(lPastingText) == false) { e.CancelCommand(); }
}
// Do the validation in a separate function which can be reused
private bool ValidateTextInput(string aTextInput)
{
if (aTextInput == null) { return false; }
Match lInvalidMatch = Regex.Match(aTextInput, this.mInvalidCharPattern);
return (lInvalidMatch.Success == false);
}
You've probably seen this already, but it's the simplest solution and has always worked for me. I catch the PreviewKeyDown event and..
<TextBox PreviewKeyDown="TextBox_PreviewKeyDown" Width="150" Height="30"></TextBox>
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
... validation here, eg. to stop spacebar from being pressed, you'd use:
if (e.Key == Key.Space) e.Handled = true;
}
I'm using this for a Windows Phone Runtime 8.1 app to allow only certain characters:
<TextBox x:Name="TextBoxTitle"
MaxLength="24"
InputScope="AlphanumericHalfWidth"
TextChanged="TextBoxTitle_TextChanged"
KeyUp="TextBoxTitle_KeyUp"
Paste="TextBoxTitle_Paste"/>
using System.Text.RegularExpressions;
bool textBoxTitle_TextPasted = false;
private void TextBoxTitle_Paste(object sender, TextControlPasteEventArgs e)
{
textBoxTitle_TextPasted = true;
}
// only allow characters A-Z, a-z, numbers and spaces
private void TextBoxTitle_TextChanged(object sender, TextChangedEventArgs e)
{
string fileNameCompatibleString = Regex.Replace(TextBoxTitle.Text, "[^a-zA-Z0-9\x20]", String.Empty);
if (TextBoxTitle.Text != fileNameCompatibleString)
{
if (textBoxTitle_TextPasted)
{
TextBoxTitle.Text = fileNameCompatibleString;
TextBoxTitle.SelectionStart = fileNameCompatibleString.Length;
}
else
{
int selectionStartSaved = TextBoxTitle.SelectionStart;
TextBoxTitle.Text = fileNameCompatibleString;
TextBoxTitle.SelectionStart = selectionStartSaved-1;
}
}
textBoxTitle_TextPasted = false;
}
// close SIP keyboard on enter key up
private void TextBoxTitle_KeyUp(object sender, KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Enter)
{
Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.IsInputEnabled = false;
Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().CoreWindow.IsInputEnabled = true;
e.Handled = true;
}
}
Going after the TextBox PreviewKeyUp event worked well. Captured the current text in the text box by casting the sender as a TextBox. Then used a RegEx replace to replace invalid characters. Could also add some tool tip text in here as well, but for now this removes invalid characters and turns the background red for instant user feedback.