Is it possible to style/template a TextBox so that input fills its value from right to left? My question is not related to the Arabic writing - I'm trying to make a textbox for a currency field, so that when a user types '12' - the value becomes '0.12'. C#/WPF/MVVM project here
Use FlowDirection.
<TextBox Text="" FlowDirection="RightToLeft" />
For more info check this link Bidirectional Features in WPF Overview
Did you try HorizontalContentAlignment? It should work for you.
<TextBox HorizontalContentAlignment="Right" Text="6999958"></TextBox>
For converting the value '12' to '0.12', please use a converter like
<TextBox HorizontalContentAlignment="Right" Text="6999958" Converter={Binding CurrencyConverter}></TextBox>
And here goes the converter code :
public class CurrencyConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var intValue = int.Parse(value.ToString());
var result = 0;
try
{
result = intValue/100;
}
catch (Exception)
{
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Tried multiple solutions here on SO, and IMHO - this one is the best for currency formatting in TextBox. This solution formats the input in currency value, and also accepts only numbers and specified divider (which is customizable in KeyPress event handler). Just give a try, easy to implement and works great for this case ( Supports the format in specific culture rather than your current computer's culture)
private void textBox_TextChanged(object sender, EventArgs e)
{
textBox.Text = string.Format(System.Globalization.CultureInfo.GetCultureInfo("id-ID"), "{0:##0.00}", double.Parse(textBox.Text));
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && (e.KeyChar != '.'))
{
e.Handled = true;
}
// only allow one decimal point
if ((e.KeyChar == '.') && ((sender as TextBox).Text.IndexOf('.') > -1))
{
e.Handled = true;
}
}
Related
I have a text box like
<TextBox Text="{Binding TransactionDetails.TransactionAmount, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" Grid.Column="3" Grid.ColumnSpan="2" Grid.Row="5"
x:Name="TextBoxAmount"/>
And I have taken "TransactionAmount" as Double. Its working well on integer value but when I am typing some Floating point value like 100.456 I am not able to type '.'
You are updating your property every time the value changes. When you type in a ., it is written into your viewmodel and the view is updated.
e.g. if you type in 100. it is rounded to 100, thus you won't see any dot ever.
You have some options to change this behavior:
use a deferred binding:
<TextBox Text="{Binding Path=TransactionDetails.TransactionAmount,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
Delay=250}"
Grid.Column="3"
Grid.ColumnSpan="2"
Grid.Row="5"
x:Name="TextBoxAmount" />
only change the value if it is different from the saved one
(I'd recommend this for every binding):
private double _transactionAmount;
public double TransactionAmount
{
get { return _transactionAmount; }
set
{
if (_transactionAmount != value)
{
_transactionAmount = value;
Notify("TransactionAmount");
}
}
or use some kind of validation, e.g. ValidatesOnExceptions.
The best solution I got by using StringFormat like
<TextBox Text="{Binding TransactionDetails.TransactionAmount, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,StringFormat=N2}" Grid.Column="3"
Grid.ColumnSpan="2" Grid.Row="5" x:Name="TextBoxAmount" />
Also we can go for custom string format as per requirements
The behavior is as expected when your app targets .NET 4.0 or earlier but was changed later on (MSDN). The old behavior can be restored by setting:
System.Windows.FrameworkCompatibilityPreferences.KeepTextBoxDisplaySynchronizedWithTextProperty = false;
Set this as early as possible (e.g. in the constructor of App.cs), otherwise the framework raises an exception.
Source and detailed explanation: https://www.mobilemotion.eu/?p=1855
Your Problem is with UpdateSourceTrigger.
Instead of using there You can use something like this,
private double amount;
public double Amount
{
get
{
return amount;
}
set
{
amount= value;
PropertyChanged();
Calculation();
}
}
PropertyChanged() You will get this from INotifyPropertyChanged. For more Information click here
https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx
you can replace the default converter with this class when binding to double type source data. This is more convient to use. below is the code:
public class double2txtConverter : IValueConverter
{
string _strCache;
double _dCache;
//Convert double to string of textbox.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (_dCache == (double)value)
return _strCache;
else
return value.ToString();
}
//convert string to double;
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
_strCache = (string)value;
_dCache = double.Parse(_strCache);
return _dCache;
}
}
//below is codebehind usage:
Binding bd = new Binding(path);
bd.Converter = new double2txtConverter();
bd.Source = source;
bd.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
txtTarget.SetBinding(TextBox.TextProperty, bd);
I am giving answer based on the answer of Herm above. The explanation is correct, but using Delay on control won't solve the issue fully. If the end user types 0.005, the delay required will be more, otherwise it will re-write value as 0.
Instead, Use a string property for binding and try to parse it to double and based on parse output set the long value you need. Put all kind of validations you need before setting the value
private double _amount;
private string _amountString;
public string Amount
{
get { return _amountString;}
set {
double d=0;
if(Double.TryParse(value, out d))
{
_amountString=value;
_amount=d;
}
}
}
}
In binding of the property use, UpdateSourceTrigger=LostFocus. It will update the property once textbox is out of focus.
I resolved this by additonal property.
public class DoubleTextBox : TextBox
{
public string DoubleText
{
get => (string)GetValue(DoubleTextProperty);
set => SetValue(DoubleTextProperty, value);
}
public static readonly DependencyProperty DoubleTextProperty =
DependencyProperty.Register(
nameof(DoubleText),
typeof(string),
typeof(DoubleTextBox),
new FrameworkPropertyMetadata(
string.Empty,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(OnDoubleTextChanged),
null,
true,
UpdateSourceTrigger.LostFocus));
private static void OnDoubleTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
var currentText = textBox.Text;
var newText = (string)e.NewValue;
if (currentText == newText)
return;
if (
double.TryParse(currentText, out var currentDouble) &&
double.TryParse(newText, out var newDouble) &&
currentDouble == newDouble
)
return;
textBox.Text = newText;
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
this.DoubleText = this.Text;
}
}
Usage:
<myControl:DoubleTextBox DoubleText="{Binding Double1, UpdateSourceTrigger=PropertyChanged}" />
I'd like to make a slider like this:
Notice there are little "|" between each slider values, and the user can only select those value.
But I found it is no use to set the SmallChange and LargeChange.
<TextBlock FontSize="44" Text="{Binding ElementName=ColorDarknessStepsSlider, Path=Value}"/>
<Slider x:Name="ColorDarknessStepsSlider" Margin="-12,0" Minimum="3" Maximum="5"
SmallChange="1" LargeChange="1" />
I expect the user could only select 3, 4, 5 but It give me this:
I don't know what's the problem. I am OK with no "|" mark between values, but at least the user must only select the integer value 3, 4, 5. How can I do this?
You can do that with xaml [EDIT : Exist only in WPF, appologies] :
<Slider x:Name="ColorDarknessStepsSlider" Margin="-12,0" Minimum="3" Maximum="5"
SmallChange="1" LargeChange="1" TickFrequency="1" />
Or via C# [EDIT : a good way]
private void ColorDarknessStepsSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
double roundedValue = Math.Round(ColorDarknessStepsSlider.Value, 0);
//Check if it is a rounded value
if(ColorDarknessStepsSlider.Value != roundedValue )
ColorDarknessStepsSlider.Value = roundedValue ;
}
Hope it can help ;)
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
int step = 40;
(sender as Slider).Value = (e.NewValue % step != 0) ? (step - e.NewValue % step) + e.NewValue : e.NewValue;
}
and xaml
<Slider Minimum="400" Maximum="2000" ValueChanged="Slider_ValueChanged"/>
Hope it help;)
For step length, use Minimum, Maximum, SmallChange
For those "ticks", try to play with TickFrequency and TickPlacement
Try using a converter - that's how I did it.
xaml change is
<Slider x:Name="durationSlider" Margin="-12,0" Minimum="1" Maximum="12"
SmallChange="1" Value="{Binding Settings.Duration, Mode=TwoWay, Converter={StaticResource DoubleToIntConverter}}"/>
public class DoubleToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int rval = (int)value;
if (value != null)
{
return rval;
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double val = (double)value;
return (int)val;
}
}
The binded field
private int _duration;
public int Duration
{
get
{
return _duration;
}
set
{
if (value != _duration)
{
_duration = value;
}
// Do this everytime because the initial input is a float converted down to an int
NotifyPropertyChanged("Duration");
}
}
I want to make part of the text of a textblock bold. This is what i tried in the IValueConverter but it does not seem to work.
public class Highlighter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
return "Question1:<Bold>Answer1</Bold>, Question2:<Bold>Answer2</Bold>, Question3:<Bold>Answer3</Bold>";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This does not make the Answer bold.
This is how i am using it in XAML.
<TextBlock Height="Auto" Width="Auto" MaxHeight="64" Text="{Binding Path=QuestionAnswer, Mode=OneWay, Converter={x:Static Highlighter}}" />
Is there a way i can achieve this by formatting the text or by sending the TextBlock to the converter?
It is definitely possible to do with TextBlock control, but considering all the efforts you might want to switch to other control (ItemsControl for example).
Anyway, here is a solution. There are actually several problems to solve:
TextBlock.Text property is string, and you can't assign preformatted text to it
TextBlock.Inlines can accept formatted text, but it is read-only property
You'll have to format text yourself (probably there are easy ways to parse text with tags and produce formatted output as a collection of Inline objects, but I don't know any)
You can create an attached property to deal with the first 2 problems:
public static class TextBlockEx
{
public static Inline GetFormattedText(DependencyObject obj)
{
return (Inline)obj.GetValue(FormattedTextProperty);
}
public static void SetFormattedText(DependencyObject obj, Inline value)
{
obj.SetValue(FormattedTextProperty, value);
}
public static readonly DependencyProperty FormattedTextProperty =
DependencyProperty.RegisterAttached(
"FormattedText",
typeof(Inline),
typeof(TextBlockEx),
new PropertyMetadata(null, OnFormattedTextChanged));
private static void OnFormattedTextChanged(
DependencyObject o,
DependencyPropertyChangedEventArgs e)
{
var textBlock = o as TextBlock;
if(textBlock == null) return;
var inline = (Inline)e.NewValue;
textBlock.Inlines.Clear();
if(inline != null)
{
textBlock.Inlines.Add(inline);
}
}
}
XAML would change just a bit:
<TextBlock local:TextBlockEx.FormattedText="{Binding Path=QuestionAnswer,
Mode=OneWay,
Converter={x:Static Highlighter}}" />
Note that you'll need to map you namespace where TextBlockEx is declared in xmlns:local="clr-namepace:<namespace_name>" in XAML.
Now you need to construct formatted text in converter instead of plain text to solve the last problem:
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
if(value == null)
{
return null;
}
var span = new Span();
span.Inlines.Add(new Run("Question1: "));
span.Inlines.Add(new Run("Answer1") { FontWeight = FontWeights.Bold });
span.Inlines.Add(new Run(", "));
span.Inlines.Add(new Run("Question2: "));
span.Inlines.Add(new Run("Answer2") { FontWeight = FontWeights.Bold });
span.Inlines.Add(new Run(", "));
span.Inlines.Add(new Run("Question3: "));
span.Inlines.Add(new Run("Answer3") { FontWeight = FontWeights.Bold });
return span;
}
Ya, something like this should put ya on track;
<TextBlock>
<Run Text="Question / Binding / Whatever..."/>
<Run Text="Answer / Binding / Whatever..." FontWeight="Bold"/>
</TextBlock>
I've written code to change the foreground property of a DataGrid cell if the row containing that cell meets a given rule (let's say its text must have the value "Incomplete"). I can make this work fairly easy by catching the LoadingRow event in code behind and writing my logic there, but I feel like this is not a very elegant MVVM implementation. Here's the code:
// Sets the foreground color of th 5th cell to red if the text in the cell corresponds
// to a value specified in the ViewModel.
private void dgProfile_LoadingRow(object sender, DataGridRowEventArgs e)
{
this.dgProfile.SelectedIndex = e.Row.GetIndex();
DataGridColumn column = this.dgProfile.Columns[4];
FrameworkElement fe = column.GetCellContent(e.Row);
FrameworkElement result = GetParent(fe, typeof(DataGridCell));
if (result != null)
{
DataGridCell cell = (DataGridCell)result;
if (((TextBlock)cell.Content).Text == (this.DataContext as ProfileViewModel).strIncompleteActivityStatus) cell.Foreground = new SolidColorBrush(Colors.Red);
else cell.Foreground = new SolidColorBrush(Colors.Black);
}
}
private FrameworkElement GetParent(FrameworkElement child, Type targetType)
{
object parent = child.Parent;
if (parent != null)
{
if (parent.GetType() == targetType)
{
return (FrameworkElement)parent;
}
else
{
return GetParent((FrameworkElement)parent, targetType);
}
}
return null;
}
Can someone tell me if there's a better way to implement this using the MVVM Light toolkit, perhaps through RelayCommand and some clever data binding?
Thanks in advance for the help!
You can define a template column and bind the foreground property to a value converter that returns the appropriate SolidColorBrush.
For example:
<data:DataGrid.Columns>
<data:DataGridTemplateColumn>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyProperty}"
Foreground="{Binding MyProperty, Converter={StaticResource MyConverter}}"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
And the converter:
public class MyConverter : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
string propertyValue = (string)value;
if (propertyValue == strIncompleteActivityStatus)
return new SolidColorBrush(Colors.Red);
else
return new SolidColorBrush(Colors.Black);
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
More details here:
DataGrid cell color based on cell value
i'm new to wpf and this is my first attempt of creating a custom user control. its purpose is to display two values (myText1 and myText2) with their corresponding images (myimage1, myimage2). sometimes, one of these values is not set and therefore oneimage should be hidden as well. here's my code so far:
Window1.xaml
<local:myControl myText2="Hello World!" />
myControl.xaml
<TextBlock Text="{Binding ElementName=myControl,Path=myText1}" />
<Image Source="myimage1.jpg" />
<TextBlock Text="{Binding ElementName=myControl,Path=myText2}" />
<Image Source="myimage2.jpg" />
myText1 was not set in window1.xaml and therefore the textblock remains empty. but the image is still displayed. which lines of code am i missing to hide the image if myText1 (or myText2) was not set in window1.xaml?
You have write converter for text to visibility
public class TextToVisibilityConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string && targetType == typeof(bool))
{
if (value.ToString().Equals(string.Empty))
return Visibility.Hidden;
else
return Visibility.Hidden;
}
else
{
return null;
}
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Visibility && targetType == typeof(string))
{
if ((Visibility)value == Visibility.Visible)
{
return "Text";
}
else
{
return string.Empty;
}
}
else
{
return null;
}
}
}
And in XAML < TextToVisibilityConverter x:Key="myCnverter"/>
Couple of small mistakes there:
if (value is string && targetType == typeof(bool))
{
if (value.ToString().Equals(string.Empty))
return Visibility.Hidden;
else
return Visibility.Hidden;
}
Should be
if (value is string && targetType == typeof(Visibility))
{
if (value.ToString().Equals(string.Empty))
return Visibility.Hidden;
else
return Visibility.Visible;
}
You need the following usings:
using System.Windows;
using System.Windows.Data;
You may also consider returning Visibility.Collapsed rather than Visibility.Hidden
Once you create the right converter then is easy.
And not much answers also got that Text.IsEmpty is available for TextBlock Text property
I created a BooleanVisibilityConverter that depends on a parameter the boolean against True or False.
Gives you the !True flexibility that is missing on xaml.
public class BooleanVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility v = Visibility.Collapsed;
bool checkValue = true;
if(parameter != null)
{
checkValue = Boolean.Parse(parameter.ToString());
}
if(value.Equals(checkValue))
{
v = Visibility.Visible;
}
return v;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then in your xaml import that namespace into:
xmlns:conv="clr-namespace:ConvertersNamespace"
Create the converter into the Resources:
<UserControl.Resources>
<conv:BooleanVisibilityConverter x:Key="bool2vis" />
</UserControl.Resources>
Then just use into your design:
<TextBlock Text="{Binding ElementName=myControl,Path=myText1}" x:Name="txtBlock"/>
<Image Source="myimage1.jpg"
Visibility="{Binding ElementName=txtBlock,Path=Text.IsEmpty,
Converter={StaticResource bool2vis},ConverterParameter=False}"/>