I have a TextBox that whenever a user types into it I want the text to only be uppercase. For example, if I type in "abc" the actual text in the TextBox and in the backend binding should be "ABC".
In WPF there is the CharacterCasing property, but I can't seem to find that in Windows XAML (or whatever you call a Windows 8 app).
I tried making a converter, but that didn't seem to work:
Converter:
public class UpperCaseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return value.ToString().ToUpper();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value.ToString().ToUpper();
}
}
XAML:
<TextBox Text="{Binding ElementName=uiMainPage, Path=Company, Mode=TwoWay, Converter={StaticResource ToUpper}}"/>
This is the code I made for it in VB.Net but it should be easy to translate to C#
Make a textchanged event for your textboxes and call a method giving it your sender as a textbox
Private Sub AnyTextBox_TextChanged(sender As Object, e As TextChangedEventArgs)
TextBoxToChange = (CType(sender,Textbox))
TextBoxToChange.Text = TextBoxToChange.Text.ToUpper()
TextBoxToChange.SelectionStart = TextBoxToChange.Text.Length
End Sub
The TextChanged event takes the textbox and changes the text to uppercase (The selectionstart is to stop the selection of the textbox to go back to 0 which causes to write backwards )
You will have XAML looking like this
<TextBox x:Name="txtTest1"
TextChanged="AnyTextBox_TextChanged"/>
<TextBox x:Name="txtTest2"
TextChanged="AnyTextBox_TextChanged"/>
It is not exactly a converter as you wish but it will do the trick just fine and this will only be 1 method per page
Related
I'm sitting in front of the following unit test without getting it to work properly
[TestMethod]
public void EvenIndexesZeroShouldHaveWhiteBackground()
{
var converterBinding = new Binding("BackgroundConverter");
converterBinding.Converter = new BackgroundConverter();
var lvi0 = new ListViewItem() { Background = Brushes.Gray };
var lv = new ListView();
lvi0.SetBinding(ListViewItem.BackgroundProperty, converterBinding);
lv.Items.Add(lvi0);
Assert.AreEqual(Brushes.White, converterBinding.Converter.Convert(lvi0, null, null, CultureInfo.InvariantCulture));
}
I was able to get another converter tested by directly calling the Convert(...) method, but it received a simple data type.
I have the feeling that I somehow need to trigger the converter when adding lvi0to the ListView(or manually afterwards) but I don't know how to do it.
Can anyone point me in the right direction?
I'm new to WPF and haven't fully gotten my head around the Bindings and Dependency Properties yet :(
[UPDATE]
The current problem is that the Convertmethod isn't called. It's not the content of the converter or the result it is giving back.
[UPDATE 2]
#Tatranskymedved comment pointed me into the right direction and calling the converter directly (as proposed by #PeterDuniho) now works. I have updated the code snippet above accordingly.
[UPDATE 3]
Here is the Converter. I HAVE to pass in a ListViewItem since this is what the it is working on. Changing it is currently not an option.
public class BackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ListViewItem item = value as ListViewItem;
if (item == null) return Brushes.White;
ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
// Get the index of a ListViewItem
if (listView == null)
return Brushes.White;
int index = listView.ItemContainerGenerator.IndexFromContainer(item);
if (index % 2 == 0)
{
return Brushes.WhiteSmoke;
}
return Brushes.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
The basic idea is: In WPF You are using some Window/UserControl, where are layouts and controls. If any of the controls should have binded its property to the ViewModel, property of the Control must be defined as DependencyProperty. If You are just using some already defined controls, You do not need to known them.
When You are creating own UserControl, it has to have a DependencyProperty, so You can bind one end to it.
Now You have to realize, what do You want to test. Is it the binding? Or the converter itself?
For the binding test, You can refer to this: Unit test WPF Bindings
Or: http://www.wpf-tutorial.com/data-binding/debugging/
However, talking about unit tests, You should test the Converter directly instead of putting them to the complicated chain objects like Binding. That is the basic motivation if the test won't work, You can say "the problem is with the Converter", not with the binding or the object You will bind to.
Only thing You need to check if the type of value You setting is the correct one. For WPF Control's BackgroundProperty it should be System.Windows.Media.Brush as on MSDN.
I require to set text color when text changes inside textbox and meets certain criterion. I can implement this from code behind with textbox_textchanged event and set brushes.color to desired color.
But I am not being able to implement this with xaml wpf approach. I am new to wpf, I'm not sure how can I set text color depending upon certain criterion when text changes in textbox.
For example: For a given textbox, when text changes, it needs to determine if input text is a number then change foreground color to green else red.
Looking forward for the help. Thank you in advance.
I am not sure whether a binding converter is allowed in your situation. But here is a solution which only needs a binding converter in your code behind.
Here is the code in xaml
<Grid.Resources>
<local:ValueConverter x:Key="ValueConverter"></local:ValueConverter>
</Grid.Resources>
<TextBox Text="{Binding Text,UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text,Converter={StaticResource ValueConverter}}" Value="True">
<Setter Property="TextBox.Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Here is the view model and the value converter
public class ViewModel : INotifyPropertyChanged
{
private string _text;
public string Text
{
get
{
return this._text;
}
set
{
this._text = value;
if (null != PropertyChanged)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Text"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (null != value)
{
if (value.ToString() == "1")
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
So the solution uses the data trigger to fulfill the goal. The only reason for using binding converter here is that you need a place to determine what kind of value should change the foreground of the TextBox. Here the foreground of TextBox will be red when the value of the TextBox is "1".
You should just be able to plug into the TextChanged event in wpf and bind a method to this event in the XAML. Then you could check to see if the new values meets your criteria and change the color accordingly.
I'm not really sure what you mean by the "XAML approach" but in this case when you simply want to attach behavior to an event thats raised on one of your controls I do not think that it is wrong to do it the way you have already tried using TextChanged. That's why events are visible in XAML in the first place.
Check the length of the string in the textbox that is being written on every input.
If it is >10 or whatever you want it to be, then change colour.
You could also make that trigger a button that was greyed out.
Sample:
MyTextBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
return new Size(MyTextBlock.DesiredSize.Width, MyTextBlock.DesiredSize.Height);
Pure xaml? , you might want to look at interactivity, Interaction, Triggers ?
Using EventTrigger in XAML for MVVM – No Code Behind
IMMO I think is better to hook up to code properties/converters/extensions,etc ... for better code reuse , but of course subjective to opinions... and at the end is always up to you.
I have a user control containing a text box and a label, the label display the length of the input text (with some formatting). I want to change the background color of the text box if the text is longer than 160 characters.
I was thinking of achieving this with bindings, but since the length of the text contain tag to be replaced I'm not willing to have 2 different binding making the same computing.
I don't succeed in changing
I can think of three way to achieves this :
1) create a hidden label with all tags replaced in his text, then have two simple converter to bind display the message length and change the background color. 3 converter for such a basic task seems too much to me.
2) Use the text_changed event to do the work. This work but it seems to me its not the way to do things in WPF.
3) Use a multibinding and pass my form as a source, this should work but looks too much 'god object' approach to me.
What do you think of that ? Am I missing a cleaner/simpler solution ?
Any suggestion is welcome, Thanks in advance.
You can create another property TBBackColor, and bind your textbox BackgroundColor to it.
Something like:
Public System.Windows.Media.Brush TBBackColor
{
get
{
return (TBText.Length>160)? new SolidColorBrush(Color.Red): new SolidColorBrush(Color.White);
}
}
And remember in your TBText property (if that is the one bind to your TextBox: Text) you need to raise propertychanged event for TBBackColor too.
Using a converter is a good idea in this case, but you won't need multiple converters. Instead, we define one converter with multiple parameters:
public class TextBoxValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrEmpty(parameter as string))
throw new ArgumentException("Invalid arguments specified for the converter.");
switch (parameter.ToString())
{
case "labelText":
return string.Format("There are {0} characters in the TextBox.", ((string)value).Count());
case "backgroundColor":
return ((string)value).Count() > 20 ? Brushes.SkyBlue : Brushes.White;
default:
throw new ArgumentException("Invalid paramater specified for the converter.");
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then in your XAML you use it like this:
<TextBox Name="textBox" Background="{Binding RelativeSource={RelativeSource Self}, Path=Text, Converter={StaticResource converter}, ConverterParameter=backgroundColor}"/>
<Label Content="{Binding ElementName=textBox, Path=Text, Converter={StaticResource converter}, ConverterParameter=labelText}"/>
I'm creating a FilteredTextBox in WPF that subclasses the included TextBox control. The FilteredTextBox must only allow characters in the range [a-zA-Z0-9_] to be entered, and I've got that part pretty much working. I've hooked into OnPreviewTextInput to handle typed characters, OnPreviewDrop to filter characters that are dragged and dropped, and I've added a PreviewExecutedHandler that runs whenever a command is executed on the control to handle pasting.
So far, so good.
The tricky part is that the control should also replace spaces with underscores when they're typed in.
I've come up with a solution, but it feels pretty hacked together and I don't know what it's missing. I feel like there's got to be a better technique that I'm just not aware of. What I've done:
internal class FilteredTextBox : TextBox
{
public FilteredTextBox()
{
CommandManager.AddPreviewExecutedHandler(this, this.HandlePreviewExecuteHandler);
}
private void HandlePreviewExecuteHandler(object sender, ExecutedRoutedEventArgs e)
{
var uiCmd = e.Command as RoutedUICommand;
if (uiCmd != null && (uiCmd.Text == "Space" || uiCmd.Text == "ShiftSpace"))
{
// We're manually handling spaces, so we need to make appropriate checks.
if (this.Text.Length == this.MaxLength) return;
if (this.SelectionLength == 0)
{
// If the user is just typing a space normally
// We need to cache CaretIndex b/c it's reset to 0 when we set Text.
var tmpIndex = this.CaretIndex;
this.Text = this.Text.Insert(tmpIndex, "_");
this.CaretIndex = tmpIndex + 1;
}
else
{
// Otherwise, replace the selected text with the underscore and fixup the caret.
this.SelectedText = "_";
this.CaretIndex += this.SelectedText.Length;
this.SelectionLength = 0;
}
e.Handled = true; // If someone hits the spacebar, say we handled it.
return;
}
}
}
Is there a smarter way?
I would bind the TextBox to a ValueConverter that eliminates whitespace on demand and replaces them with underscores.
The ValueConverter would look something like this:
public class SpaceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return System.Convert.ToString(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = System.Convert.ToString(value);
//the meat and potatoes is this line
text = text.Replace(" ", "_");
return text;
}
}
And your TextBox would bind to it this way:
<TextBox Text="{Binding Path=UserString, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource SpaceConverter}}" />
Note that UserString would have to be in the current DataContext.
Don't forget to define SpaceConverter in the XAML as well. One way to do this, assuming you're working on a UserControl, would be:
<UserControl.Resources>
<local:SpaceConverter x:Key="SpaceConverter" />
</UserControl.Resources>
Where local is defined as the namespace containing SpaceConverter.cs.
I think your logic is good, but I'd put it in an override of OnPreviewKeyDown.
Would it be better to just replace space with _ when the control loses focus or do you absolutely need it to be replaced as the user keys it in?
Scenario: A ListView is DataBound to an ObservableCollection<CustomClass> and is displaying it's items through a custom ItemTemplate. The CustomClass just contains three string properties and one boolean property and already has INotifyPropertyChanged implemented on every of it's four properties. The custom ItemTemplate of the ListView has One-Way bindings on the three string properties and a Two-Way binding on the boolean property, displaying it as a CheckBox.
Problem: I'm looking for the most elegant (in terms of WPF) way to display the count of all checked items in that ListView using a TextBlock - or in other words, all items that have their boolean property set to true in that collection. I want that TextBlock to immediately update the displayed count if one of the ListView items gets checked/unchecked. I know that there are (rather) ugly ways to achieve this with code behind and eventhandling, but I'd like to know if there's a clever way to do this maybe completely in XAML with arcane DataBinding syntax.
Edit: Just as an example/clarification: The ListView displays 100 items, 90 items have their boolean property set to true, so the TextBlock will display '90'. If the user unchecks one more item through it's CheckBox and therefore sets it's property to false through the Two-Way binding, the TextBlock should update to '89'.
Personally I would probably perform this in my ViewModel. Subscribe to the property changed on the items in the ObservableCollection, and then signal the Count property changed on the ViewModel whenever the boolean property changes. In your view simply bind to the Count property.
You could use a Converter to build up a string with the count of the checked items
public sealed class CountToStringConverter : System.Windows.Data.IValueConverter {
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
ObservableCollection<CustomClass> items = value as ObservableCollection<CustomClass>;
int count = 0;
foreach (var item in items) {
if (item.IsChecked) {
count++;
}
}
return count + " Items";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
#endregion
}
Bind the Text-Property of the TextBox to the Collection.
<TextBox Text={Binding Items, Converter={StaticResource countToStringConverter}}/>
UPDATE:
This Binding works only if the Property Items fires the PropertyChanged-Event, if the Collection is changed.
If it were a simple ASP.NET form, I'd look at using JQuery to count the selected items in the ListBox. That may still be a viable option in WPF:
var count = 0;
$('#multiItemListBox :selected').each(count++);
Plug this code into a JS event handler for the OnChange event of the ListBox. You'll have to know what the ListBox would actually be called in the HTML the client gets, and I'm not sure how WPF mashes them up or how to stick the correct reference into server-side XAML.
Thanks for all the answers I've got, these were all applicable solutions but unfortunately not really what I've tried to achieve. So this is how I've solved the problem now:
I've implemented a DependencyProperty on the Window containing the TextBlock:
public static readonly DependencyProperty ActiveItemCountProperty =
DependencyProperty.Register("ActiveItemCount", typeof(int), typeof(CustomControl), new UIPropertyMetadata(0));
On the DataTemplate for the ListView items the CheckBox registered an EventHandler for the Click-Event:
<CheckBox IsChecked="{Binding Active, Mode=TwoWay}" Click="CheckBox_Click" />
The event handler in code behind looks something like this:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<CustomClass> sourceCol = listView.DataContext as ObservableCollection<CustomClass>;
if (sourceCol != null)
ActiveItemCount = sourceCol.Count(x => x.Active);
}
And obviously, the TextBlock is just data bound to this DependencyProperty:
<TextBlock Text="{Binding Path=ActiveItemCount, ElementName=ControlRoot}" />
With ControlRoot being the name of the Window.