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");
}
}
Related
I have slider :
<Slider x:Name="DesktopAudioSlider"
HorizontalAlignment="Stretch"
Margin="0,0,5,0">
I need behavior like in UWP Slider , that follow the cursor when it click. I cant use events because use MVVM , tried to convert sender and eventargs to tuple and pass to command , but there is no way how to pass MouseEventargs to converter.
Converter :
class SliderArgsToTupleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(parameter is MouseEventArgs)
{
return new Tuple<object, MouseEventArgs>(value, parameter as MouseEventArgs);
}
else
{
throw new ArgumentException();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Command :
this.SlideMouseMoveCommand = new RelayCommand<Tuple<object, MouseEventArgs>>(SlideMouseMoveExecute);
Trigger :
<i:EventTrigger EventName="MouseMove">
<i:InvokeCommandAction Command="{Binding MixerViewModel.SlideMouseMoveCommand, Source={StaticResource Locator}}"
CommandParameter="{Binding ElementName=DesktopAudioSlider , ConverterParameter={}}"
</i:EventTrigger>
There is no way how to pass two arguments in command , i found the one solution - add behavior.
public class SliderMoveBehavior : Behavior<Slider>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseMove += SliderMouseMove;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseMove -= SliderMouseMove;
}
private void SliderMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point position = e.GetPosition(AssociatedObject);
double d = 1.0d / AssociatedObject.ActualWidth * position.X;
var p = AssociatedObject.Maximum * d;
AssociatedObject.Value = (int)p;
}
}
}
In XAML :
<Slider x:Name="PlaybackSpeedSlider">
<i:Interaction.Behaviors>
<behaviors:SliderMoveBehavior/>
</i:Interaction.Behaviors>
</Slider>
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;
}
}
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}" />
Background.
I am developing a stock trading application.
Which obviously have a market watch.
I am developing this market watch using Datagrid.
What Does the Grid do?
It displays price points of a stock.
Every time a stock value increases the particular cell foreground turns green
if it decreases it turns red.
What i did?
I tried to use the value converter method and multibinding
Problem.
The value converter gives the current value only.
How can i pass the old value to that converter.
Code:
<wpfTlKit:DataGrid.CellStyle>
<Style TargetType="{x:Type wpfTlKit:DataGridCell}">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource myHighlighterConverter}"
>
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}"></Binding>
<Binding Path="Row" Mode="OneWay"></Binding>
<Binding ElementName="OldData" Path="Rows"></Binding>
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</wpfTlKit:DataGrid.CellStyle>
Converter
public class HighlighterConverter : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[1] is DataRow)
{
//Change the background of any cell with 1.0 to light red.
var cell = (DataGridCell)values[0];
var row = (DataRow)values[1];
var columnName = cell.Column.SortMemberPath;
if (row[columnName].IsNumeric() && row[columnName].ToDouble() == 1.0)
return new SolidColorBrush(Colors.LightSalmon);
}
return SystemColors.AppWorkspaceColor;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
public static class Extensions
{
public static bool IsNumeric(this object val)
{
double test;
return double.TryParse(val.ToString(), out test);
}
public static double ToDouble(this object val)
{
return Convert.ToDouble(val);
}
}
To change the color in a DataGrid cell I recommend the following:
Build a Model that implements INotifyPropertyChanged that contains the current, and previous price plus a property that reflects the change in the price (I've attached the full model at the end of this answer).
public double ChangeInPrice
{
get
{
return CurrentPrice - PreviousPrice;
}
}
And set the Background of the CellTemplate in your DataGrid based on the change in price using a Converter.
Note: INotifyPropertyChanged helps to change the color of the cell when the price values change.
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Path=CurrentPrice}"
Background="{Binding Path=ChangeInPrice, Converter={StaticResource backgroundConverter}}" >
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
[ValueConversion(typeof(object), typeof(SolidBrush))]
public class ObjectToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
SolidColorBrush b = Brushes.White;
try
{
double stock = (double)value;
if (stock > 0)
{
b = Brushes.Green;
}
else if (stock < 0)
{
b = Brushes.Red;
}
}
catch (Exception e)
{
}
return b;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here is full model for completeness:
public class Stock : INotifyPropertyChanged
{
public Stock(string stockName, double currentPrice, double previousPrice)
{
this.StockName = stockName;
this.CurrentPrice = currentPrice;
this.PreviousPrice = previousPrice;
}
private string _stockName;
public String StockName
{
get { return _stockName; }
set
{
_stockName = value;
OnPropertyChanged("StockName");
}
}
private double _currentPrice = 0.00;
public double CurrentPrice
{
get { return _currentPrice; }
set
{
_currentPrice = value;
OnPropertyChanged("CurrentPrice");
OnPropertyChanged("ChangeInPrice");
}
}
private double _previousPrice = 0.00;
public double PreviousPrice
{
get { return _previousPrice; }
set
{
_previousPrice = value;
OnPropertyChanged("PreviousPrice");
OnPropertyChanged("ChangeInPrice");
}
}
public double ChangeInPrice
{
get
{
return CurrentPrice - PreviousPrice;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
well i think the problem is not the datagrid, but the object you bind to. if you bind to a datatable the oldvalues are built in (DataRowVersion). if you have other entity objects, then this entities need to support the original and modified values.
I have two radiobuttons which are bound to the same property .There are 2 converters for the radio buttons. But the conversion for the 2 nd checkbox only happens the 1st time . Is there something wrong in the code.
<RadioButton Margin="5,1" GroupName="groupValueOrTime" Name="radioButtonTimeDriven" VerticalAlignment="Top" IsChecked="{Binding Path=TriggerType ,Converter={StaticResource dailyTriggerConverter}}" Grid.Column="0" Grid.Row="0" >Time Driven</RadioButton>
<RadioButton Margin="5,1" GroupName="groupValueOrTime" Name="radioButtonValueDriven" VerticalAlignment="Top" Grid.Column="0" Grid.Row="1" IsChecked="{Binding Path=TriggerType,Converter={StaticResource valueDrivenTriggerConverter}}" >Value Driven</RadioButton>
Code:
public class TriggerTypeDailyToBoolProperty:IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//convert from TriggerType to bool:
int TriggerType=int.Parse(value.ToString());
if (TriggerType == 0 || TriggerType == 1 || TriggerType == 2 || TriggerType == 3 || TriggerType == 4 || TriggerType == 5 || TriggerType == 6)
return true;
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool isChecked = (bool)value;
return (isChecked? 0: -1);
}
#endregion
}
public class TriggerTypeValueDrivenToBoolProperty : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//convert from TriggerType to bool:
int TriggerType = int.Parse(value.ToString());
if (TriggerType == 9)
return true;
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool isChecked = (bool)value;
return (isChecked ? 9 : -1);
}
#endregion
}
Combining grouping, radio buttons, and binding doesn't work. An odd feature of binding is that if you set the value of a bound target property in code, it disables the binding. The assumption that the design of binding appears to be making is that the only ways you should change a target property is by using the UI or by changing the source property it's bound to, and if a piece of code explicitly sets the value of a bound target property, it knows what it's doing.
Unfortunately, this means that grouped radio buttons, which set each others' values in code when they're changed, break their bindings when the user clicks on them. Oops.
The solution's simple: bind the radio buttons to properties in your view model, eliminate the grouping, and put logic in the view model to handle the mutually-exclusive nature of the bound properties, e.g.:
private bool _Option1;
public bool Option1
{
get { return _Option1; }
set
{
if (value != _Option1)
{
_Option1 = value;
if (value)
{
Option2 = false;
Option3 = false;
}
}
OnPropertyChanged("Option1");
}
}
I think you will require to have two different boolean properties in your view model.
Please refer below example depicting viewmodel.
public class TriggerViewModel : INotifyPropertyChanged
{
private bool _triggerTypeValue;
private bool _triggerTypeTime;
public bool TriggerTypeValue
{
get
{
return _triggerTypeValue;
}
set
{
_triggerTypeValue = value;
OnPropertychanged("TriggerTypeValue");
SetTriggerTypeTime(!_triggerTypeValue);
}
}
public bool TriggerTypeTime
{
get
{
return _triggerTypeTime;
}
set
{
_triggerTypeTime = value;
OnPropertychanged("TriggerTypeTime");
SetTriggerTypeValue(!_triggerTypeTime);
}
}
public TriggerViewModel()
{
_triggerTypeValue = false;
_triggerTypeTime = true;
}
private void SetTriggerTypeTime(bool value)
{
_triggerTypeTime = value;
OnPropertychanged("TriggerTypeTime");
}
private void SetTriggerTypeValue(bool value)
{
_triggerTypeValue = value;
OnPropertychanged("TriggerTypeValue");
}
private void OnPropertychanged(string propertyName)
{
if(PropertyChanged!= null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
For XAML
<RadioButton Margin="5,1" GroupName="groupValueOrTime" Name="radioButtonTimeDriven"
VerticalAlignment="Top" IsChecked="{Binding Path=TriggerType}"
Grid.Column="0" Grid.Row="0">
Time Driven
</RadioButton>
<RadioButton Margin="5,1" GroupName="groupValueOrTime" Name="radioButtonValueDriven"
VerticalAlignment="Top" Grid.Column="0" Grid.Row="1"
IsChecked="{Binding Path=TriggerType}">
Value Driven
<RadioButton>
Note: This approach removes Converters and allow you to keep your business logic in central view model.