I have this XAML for acolumn into DataGrid
<DataGridTemplateColumn Header="% Deduccion Anticipo">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding NumPorcentajeAnticipo, Mode=TwoWay, StringFormat={}{0:00.}%}" Visibility="{Binding Merlin_ConceptosFacturacion.BitOtrosItms_Anticipos,Converter={StaticResource boolToVisibility}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding NumPorcentajeAnticipo, Mode=TwoWay,StringFormat={}{0:00.}%}" Visibility="{Binding Merlin_ConceptosFacturacion.BitOtrosItms_Anticipos,Converter={StaticResource boolToVisibility}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The Stringformat applys as i expect, but muy problem is the user can fill it with any char alpha, number symbol, how can i do to prevent it, it is posible set an inputmask ?
I'm was trying with another StringFormats but any one of them work as I expect.
UPDATE: The column is currently binded to a Numeric property of my view model.
You can use the KeyDown event of a TextBox to intercept and filter out invalid values. You could even create your own derived TextBox and override OnKeyDown for a better encapsulated solution.
None of the built-in controls have the ability to specify an input mask. But, there are masked input controls out there on the internet for WPF. We are using the Telerik Rad Controls for WPF package and it has such a control. I use it in my application and it works very well.
you can achieve your requirement by using following methods.
Put the masked textbox in CellEditingTemplate and set the mask in that masked textbox.
Create the custom render based on your requirement and bind to the CellEditingTemplate.
After some research found this on another question:
Numeric Data Entry in WPF And #Brian Hinchey answer match with some of my needs.
Just add by myself some Culture validations for decimal numbers plus some editing and validation tools. Hope this help somebody else.
To use it:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<controls:NumericTextBox DecimalPlaces="2" DecimalSeparator="."/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
If no decimal places or separator are provided, it take CultureInfo.CurrentCulture parms
Here is the final code:
public class NumericTextBox : TextBox
{
#region Formato
private string previousText = "";
private bool ApplyingFormat = false;
private CultureInfo _CI = new CultureInfo(CultureInfo.CurrentCulture.LCID,true);
public CultureInfo CI
{
get { return _CI; }
set { _CI = value; }
}
private int _DecimalPlaces = 0;
/// <summary>
/// Numero de plazas decimales
/// </summary>
public int DecimalPlaces
{
get { return _DecimalPlaces; }
set { _DecimalPlaces = value; _CI.NumberFormat.NumberDecimalDigits = value; }
}
public Decimal DecimalValue = 0;
private string _DecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
public string DecimalSeparator
{
get { return _DecimalSeparator; }
set { _DecimalSeparator = value; _CI.NumberFormat.NumberDecimalSeparator = _DecimalSeparator; }
}
//public string DecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
#endregion
public NumericTextBox()
{
HorizontalContentAlignment = HorizontalAlignment.Right;
DataObject.AddPastingHandler(this, OnPaste);
}
private void OnPaste(object sender, DataObjectPastingEventArgs dataObjectPastingEventArgs)
{
var isText = dataObjectPastingEventArgs.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);
if (isText)
{
var text = dataObjectPastingEventArgs.SourceDataObject.GetData(DataFormats.Text) as string;
if (IsTextValid(text))
{
return;
}
}
dataObjectPastingEventArgs.CancelCommand();
}
private bool IsTextValid(string enteredText)
{
// If keyboard insert key is in toggled mode, and the actual insert point is Decimalseparator, we must avoid to overwrite it
if (SelectionStart == this.Text.IndexOf(DecimalSeparator)
& System.Windows.Input.Keyboard.GetKeyStates(System.Windows.Input.Key.Insert) == System.Windows.Input.KeyStates.Toggled)
{
SelectionStart += 1;
}
if (!enteredText.All(c => Char.IsNumber(c) || c == DecimalSeparator.ToCharArray()[0] || c == '-'))
{
return false;
}
//We only validation against unselected text since the selected text will be replaced by the entered text
var unselectedText = this.Text.Remove(SelectionStart, SelectionLength);
if ( enteredText == DecimalSeparator && unselectedText.Contains(DecimalSeparator))
{
// Before return false, must move cursor beside Decimal separator
SelectionStart = this.Text.IndexOf(DecimalSeparator) + 1;
return false;
}
if (enteredText == "-" && unselectedText.Length > 0)
{
return false;
}
return true;
}
private bool ApplyFormat(TextChangedEventArgs e)
{
if (!ApplyingFormat)
{
ApplyingFormat = true;
int SelectionStartActual = SelectionStart;
string FinallText = this.Text;
if (!FinallText.Contains(DecimalSeparator) & DecimalPlaces > 0)
{
FinallText = String.Format("{0}{1}{2}", this.Text, DecimalSeparator, new string('0', DecimalPlaces));
}
bool state = Decimal.TryParse(FinallText, NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint | NumberStyles.AllowTrailingSign, _CI, out DecimalValue);
DecimalValue = Math.Round(DecimalValue, DecimalPlaces);
if (DecimalValue == 0)
{
FinallText = "";
}
else
{
if (FinallText != DecimalValue.ToString(_CI))
{
FinallText = DecimalValue.ToString(_CI);
}
}
if (FinallText != this.Text)
{
this.Text = FinallText;
SelectionStart = SelectionStartActual;
}
previousText = this.Text;
ApplyingFormat = false;
return state;
}
else
{
return true;
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
e.Handled = !ApplyFormat(e);
base.OnTextChanged(e);
}
protected override void OnPreviewTextInput(System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !IsTextValid(e.Text);
base.OnPreviewTextInput(e);
}
}
Related
I've read about triggers/behaviors and it only seems to change the current entry box properties.
But in my case, i want to make a label appear under my entry box if the value is >8 in REAL TIME.
For my others validation i am using Fluent Validation and the validation is done when the user click the save button BUT here this is not what i want to do since the >8 is only a warning and can be saved.
So i have to find a way to display the warning as soon as the user entered a number >8 in the entry box.
Is there a way to do this in xamarin.forms ? Also, maybe there's a way to do this with FluentValidation but not sure.
Thanks.
Edit trying to implement with TextChanged
xaml
<control:MaskedEntry Placeholder="HH:MM:SS" Mask="XX:XX:XX" Keyboard="Numeric" Text="{Binding TaskDuration}" TextChanged="DurationIs8"></control:MaskedEntry>
<Label x:Name="errorMessage" Text="Greater than 8" IsVisible="False" ></Label>
xaml.cs
private void DurationIs8(object sender, TextChangedEventArgs e)
{
var entryText = ((Entry)sender).Text;
var value = Helper.GetDuration(entryText);
if(value.TotalHours > 8)
{
errorMessage.IsVisible = true;
}
}
The GetDuration method to convert the entry box to hours
public static TimeSpan GetDuration(string duration)
{
var value = duration.Split(':').Select(int.Parse).ToArray();
var datetime = new TimeSpan(value[0], value[1], value[2]);
return datetime;
}
private void DurationIs8(object sender, TextChangedEventArgs e)
{
var entryText = ((Entry)sender).Text;
if (entryText == null || entryText.Length < 8) return;
var value = Helper.GetDuration(entryText);
if(value != null && value.TotalHours > 8)
{
errorMessage.IsVisible = true;
}
}
public static TimeSpan GetDuration(string duration)
{
var value = duration.Split(':').Select(int.Parse).ToArray();
if (value.Length < 3) return TimeSpan.Zero;
var datetime = new TimeSpan(value[0], value[1], value[2]);
return datetime;
}
I created a editable ComboBox for searching(filtering) like Google. I am using the ActionHandler "KeyUp" and the first input is highlighted and overwritten. How can i disable the overwriting or highlighting?
private void CbInKuLi_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
CollectionView itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(cbInKuLi.ItemsSource);
itemsViewOriginal.Filter = ((o) =>
{
if (String.IsNullOrEmpty(cbInKuLi.Text)) return true;
else
{
DeKreditor x = (DeKreditor)o;
string filterText = cbInKuLi.Text;
if (x.Nummer.ToLowerInvariant().Contains(filterText)
|| (!string.IsNullOrWhiteSpace(x.Firma) && x.Firma.ToLowerInvariant().Contains(filterText))
|| (!string.IsNullOrWhiteSpace(x.Vorname) && x.Vorname.ToLowerInvariant().Contains(filterText))
|| (!string.IsNullOrWhiteSpace(x.Name) && x.Name.ToLowerInvariant().Contains(filterText)))
return true;
else
return false;
}
});
itemsViewOriginal.Refresh();
cbInKuLi.IsDropDownOpen = true;
}
XAML:
<ComboBox
x:Name="cbInKuLi"
StaysOpenOnEdit="True"
IsEditable="True"
IsTextSearchEnabled="False"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Grid.Row="0"
Grid.Column="1"
Margin="5,0,5,5"
SelectionChanged="CbInKuLi_SelectionChanged"
KeyUp="CbInKuLi_KeyUp"
TextOptions.TextFormattingMode="Ideal" />
The Text Highlight caused because of setting IsDropDownOpen to true.
The Editable ComboBox auto select the Text if it is Opened, so you could get the TextBox from the template of the ComboBox by its Name than set the selection length to zero at the end of the text.
private void CbInKuLi_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
CollectionView itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(cbInKuLi.ItemsSource);
itemsViewOriginal.Filter = ((o) =>
{
if (String.IsNullOrEmpty(cbInKuLi.Text)) return true;
else
{
Model x = (Model)o;
string filterText = cbInKuLi.Text;
if (x.Text.ToLowerInvariant().Contains(filterText))
return true;
else
return false;
}
});
itemsViewOriginal.Refresh();
cbInKuLi.IsDropDownOpen = true;
var textbox = (TextBox)cbInKuLi.Template.FindName("PART_EditableTextBox", cbInKuLi);
textbox.Select(textbox.Text.Length, textbox.Text.Length);
}
UPDATE:
From comments you can replace the last line by the following line and it is better than the original one:
textbox.CaretIndex = textbox.Text.Length;
Code in context
private void button2_Click(object sender, RoutedEventArgs e)
{
edit();
}
public void edit()
{
textBox1.IsEnabled = true;
textBox2.IsEnabled = true;
textBox3.IsEnabled = true;
textBox4.IsEnabled = true;
textBox5.IsEnabled = true;
textBox6.IsEnabled = true;
textBox7.IsEnabled = true;
textBox8.IsEnabled = true;
textBox9.IsEnabled = true;
textBox10.IsEnabled = true;
textBox11.IsEnabled = true;
textBox12.IsEnabled = true;
textBox13.IsEnabled = true;
textBox14.IsEnabled = true;
textBox15.IsEnabled = true;
textBox16.IsEnabled = true;
textBox17.IsEnabled = true;
textBox18.IsEnabled = true;
}
I want perform the above using a simple for loop that loops through 1-18.
I have tried the followng method but doesn't work as intended
for(i=0;i<19;i++)
{
textBox"" + i + "".IsVisible = true;
}
I'm new to wpf and i'm migrating my app from winforms to wpf.
Use binding.
XAML (MyUserControl):
<UserControl Name="MyControl" ...
....
<TextBox Name="textBox1" IsEnabled="{Binding ElementName=MyControl, Path=AreTextBoxesEnabled}" ... />
<TextBox Name="textBox2" IsEnabled="{Binding ElementName=MyControl, Path=AreTextBoxesEnabled}" ... />
<TextBox Name="textBox3" IsEnabled="{Binding ElementName=MyControl, Path=AreTextBoxesEnabled}" ... />
...
Code-behind (MyUserControl):
public static readonly DependencyProperty AreTextBoxesEnabledProperty = DependencyProperty.Register(
"AreTextBoxesEnabled",
typeof(bool),
typeof(MyUserControl));
public bool AreTextBoxesEnabled
{
get { return (bool)GetValue(AreTextBoxesEnabledProperty); }
set { SetValue(AreTextBoxesEnabledProperty, value); }
}
Just calling AreTextBoxesEnabled = true; will make all the textboxes enabled.
Of course, there are many many other ways. But this is the basic way (without MVVM) of doing it, by harnessing the power of binding.
Simple solution (but not recommended) way is as simple as:
for (i = 0; i < 19; i++)
{
var tb = this.FindName("textBox" + i.ToString()) as TextBox;
if (tb != null) tb.IsEnabled = true;
}
Create a list of text boxes like:
var textBoxes = new List<TextBox>();
// Btw, I don't have a compiler by hand, I assume the type is TextBox.
Fill textBoxes:
textBoxes.Add(textBox1);
textBoxes.Add(textBox2);
...
textBoxes.Add(textBox18);
This is a one-time manual action to fill it. Afterwards you can loop through them:
foreach (var textBox in textBoxes)
{
textBox.IsVisible = true;
}
Or use any other setting/algorithm on the text boxes with the foreach loop (or for, linq etc).
I am using a RadGridView to display some string data in a column. I am using databinding. Some of my text strings have portions of the text encased with {} and I would like to display this text in a different color.
From looking around on the net I have found that I can change text color of text in a text block but Im having trouble applying this to a databound datagrid column.
Could anyone advise if this is possible.
---EDIT---
Heres xaml where I define datacolumn:
<telerik:GridViewDataColumn x:Name="colMasterValue" Header="Localise - Master Value" DataMemberBinding="{Binding MasterValue}" ShowDistinctFilters="False" IsReadOnly="True"/>
Heres the display:
So what I want is {Customer.Panel.field} to appear in a different color.
Let's try the next solution.
Will use a custom TextBlock that can separate regular character and the '{' or '}'.
Let's add the replace mechanism that can replace the original text with decorated text. Where all text except the '{' or '}' colored in some way.
Here is the xaml code:
<telerik:GridViewDataColumn DataMemberBinding="{Binding Path=Text}"
Header="HeaderName"
IsFilterable="False" IsReorderable="False">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate DataType="flowConfiguration:SomeDataType">
<modules:MyTextBlock Text="{Binding Text,
UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
Here is the MyTextBlock code
public class MyTextBlock:TextBlock
{
private DependencyPropertyDescriptor _descriptor;
private bool _isUpdating;
public MyTextBlock()
{
this.Unloaded += OnUnloaded;
_descriptor = DependencyPropertyDescriptor.FromProperty(TextProperty, typeof(TextBlock));
_descriptor.AddValueChanged(this, OnValueChanged);
}
private void OnValueChanged(object sender, EventArgs eventArgs)
{
if(_isUpdating) return;
_isUpdating = true;
var text = Text;
if(string.IsNullOrEmpty(text)) return;
Inlines.Clear();
UpdateInlines(text);
_isUpdating = false;
}
private void UpdateInlines(string text)
{
//text = #"{Customer.Panel.field}";
var runs = new List<Run>();
var sb = new StringBuilder();
foreach (var current in text)
{
if (current.Equals('}') || current.Equals('{'))
{
if (sb.Length == 0)
{
runs.Add(new Run
{
Text = current.ToString()
});
}
else
{
runs.Add(new Run
{
Text = sb.ToString(),
Foreground = Brushes.Red
});
runs.Add(new Run
{
Text = current.ToString()
});
sb.Clear();
}
}
else
{
sb.Append(current);
}
}
if(sb.Length > 0)
runs.Add(new Run{Text = sb.ToString(), Foreground = Brushes.Red});
runs.ForEach(run =>
Inlines.Add(run));
}
private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
{
this.Unloaded -= OnUnloaded;
_descriptor.RemoveValueChanged(this, OnValueChanged);
}
}
regards.
Lets say I have 3 or more Slider and each slider can have a value from 0 to 100.
However I want that the sum of all slider values is <= 100. In case I have 4 slider everyones max value would be 25.
Every slider has a binding to a double variable and every time the user uses a slider (tick frequency 0.1) I calculate the sum and set other slider back or if necessary set the same slider back, so that the sum is <= 100.
The problem is, that the calculation needs a decent amount of time and in the meantime the user can set illegal values. I would like to solve this by blocking the UI until the calculation is over. Basically the opposite of the desired responsiveness.
Other ideas and suggestions to solve the slider thing are welcome.
slider binding
public BindingList<WLCToolParameter> WLCParameter
{
get { return _toolParameter; }
set { _toolParameter = value; }
}
should be instant - not really :(
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MCDA.Entity;
using MCDA.Extensions;
namespace MCDA.Model
{
class ProportionalDistributionStrategy : IWeightDistributionStrategy
{
public void Distribute<T>(IList<T> listOfToolParameter) where T : class, IToolParameter
{
if (listOfToolParameter.Count == 0)
return;
IToolParameter lastWeightChangedToolParameter = lastWeightChangedToolParameter = listOfToolParameter[0].LastWeightChangedToolParameter;
double sumOfAllWeights = listOfToolParameter.Sum(t =>t.Weight);
//we have to rescale
if (sumOfAllWeights > 100)
{
double overrun = sumOfAllWeights - 100;
//how much do we have without the locked and the last changed?
double availableSpace = listOfToolParameter.Where(t => t.IsLocked == false && t != lastWeightChangedToolParameter).Sum(t => t.Weight);
//we have enough by taking from the non locked
if (availableSpace > overrun)
{
//lets remove proportional
double sumOfChangeableWeights = listOfToolParameter.Where(t => t.IsLocked == false && t != lastWeightChangedToolParameter).Sum(t => t.Weight);
//in case we have only one element that is suitable we can directly remove all from this one
if (listOfToolParameter.Where(t => t.IsLocked == false && t.Weight > 0 && t != lastWeightChangedToolParameter).Count() == 1)
{
listOfToolParameter.Where(t => t.IsLocked == false && t.Weight > 0 && t != lastWeightChangedToolParameter).ForEach(t => t.Weight = t.Weight - overrun);
return;
}
listOfToolParameter.Where(t => t.IsLocked == false && t.Weight > 0 && t != lastWeightChangedToolParameter).ForEach(t => t.Weight = t.Weight - (sumOfChangeableWeights / (sumOfChangeableWeights - t.Weight)) * overrun);
}
//we have to resize also the latest change, but we try to keep as much as possible of the latest change
else
{
//lets set them to zero
listOfToolParameter.Where(t => t.IsLocked == false && t != lastWeightChangedToolParameter).ForEach(t => t.Weight = 0);
//how much are we still over?
double stillOver = listOfToolParameter.Sum(t => t.Weight) - 100;
//and cut from the last changed
listOfToolParameter.Where(t => t == lastWeightChangedToolParameter).ForEach(t => t.Weight -= stillOver);
}
}
}
}
}
It looks like you are not making use of data binding. Here is a simple example - just add your calculation logic to the calculating method. The UI will update itself. Note this is a crude example. I am not sure I would implement it this way. Also be careful of using decimals in your numbers. If you use this with foreign languages / regional settings with a comma as the decimal separator - it will error out.
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<Slider Margin="10" Value="{Binding Path=Value1}" />
<TextBlock Text="{Binding Path=Value1}" />
<Slider Margin="10" Value="{Binding Path=Value2}" />
<TextBlock Text="{Binding Path=Value2}" />
<Slider Margin="10" Value="{Binding Path=Value3}" />
<TextBlock Text="{Binding Path=Value3}" />
</StackPanel>
</Grid>
</Window>
Code Behind (MVVM approach this would be in your View Model)
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private double _value1;
public double Value1
{
get { return _value1; }
set
{
if(value != _value1)
{
_value1 = value;
DoMyCalculations(_value1, _value2, _value3);
NotifyPropertChanged("Value1");
}
}
}
private double _value2;
public double Value2
{
get { return _value2; }
set
{
if (value != _value2)
{
_value2 = value;
DoMyCalculations(_value1, _value2, _value3);
NotifyPropertChanged("Value2");
}
}
}
private double _value3;
public double Value3
{
get { return _value3; }
set
{
if (value != _value3)
{
_value3 = value;
DoMyCalculations(_value1, _value2, _value3);
NotifyPropertChanged("Value3");
}
}
}
private bool isCalculating = false;
private void DoMyCalculations(double value1, double value2, double value3)
{
if (isCalculating)
return;
isCalculating = true;
// Perform logic to reset here
isCalculating = false;
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notify of Property Changed event
/// </summary>
/// <param name="propertyName"></param>
public void NotifyPropertChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}