0

I am using MVVM/Caliburn.Micro in a silverlight 5 project and I have a requirement to automatically change the text the user enters in a silverlight textbox to uppercase.

First, I thought I could just set the backing variable on the ViewModel to uppercase and the two way binding would change the text. That didn't work (though I believe it will if I use a lost focus event, but I cannot do that since I have other things I must do for KeyUp as well and attaching two events results in a xaml error)

Since that didn't work I tried calling a method on the KeyUp event. This technically works, but since it is replacing the text it puts the cursor back at the beginning, so the user ends up typing backwards.

This seems like fairly simple functionality - how do I transform the text a user types into uppercase? Am I missing something easy?

Here is my existing code. Xaml:

<TextBox x:Name="SomeName" cal:Message.Attach="[Event KeyUp] = [Action ConvertToUppercase($eventArgs)]" />

View Model:

public void ConvertToUppercase(System.Windows.Input.KeyEventArgs e)
{
     SomeName = _someName.ToUpper();
     //Other code that doesn't concern uppercase
}



EDIT FOR ALTERNATE SOLUTION: McAden put forth a nice generic solution. I also realized at about the same time that there was an alternate solution (just pass the textbox as a param to the uppercase method and move the cursor), so here is the code for that as well:

xaml:

<TextBox x:Name="SomeName" cal:Message.Attach="[Event KeyUp] = [Action ConvertToUppercase($source, $eventArgs)]; [Event KeyDown] = [Action DoOtherStuffThatIsntQuestionSpecific($eventArgs)]" />

cs method:

public void ConvertToUppercase(TextBox textBox, System.Windows.Input.KeyEventArgs e)
{
    //set our public property here again so the textbox sees the Caliburn.Micro INPC notification fired in the public setter
    SomeName = _someName.ToUpper();

    //move the cursor to the last so the user can keep typing
    textBox.Select(SomeName.Length, 0);
}

and of course cs standard Caliburn.Micro property:

private String _someName = "";
public String SomeName
{
    get
    {
        return _someName;
    }
    set
    {
        _someName = value;
        NotifyOfPropertyChange(() => SomeName);
    }
}
Mario
  • 3,405
  • 6
  • 38
  • 48
  • take a look at [`NotifyPropertyChanged`](http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.95).aspx) ;) – Silvermind Oct 23 '12 at 20:50
  • http://stackoverflow.com/questions/4097597/character-casing-in-silverlight-4-textbox – McAden Oct 23 '12 at 20:54
  • @Silvermind, - that was what I tried first (exactly like devdigital's suggested answer) but it doesn't reflect the change in the UI – Mario Oct 23 '12 at 21:02
  • @McAden - I saw that answer but like I said, I unfortunately have some other code that must be executed on key press and adding two events in the xaml won't compile so that doesn't seem to be an option – Mario Oct 23 '12 at 21:02
  • You can attach two EventTriggers to the same event just fine. See my answer as an example. – McAden Oct 23 '12 at 21:11
  • When using `NotifyPropertyChanged` have you set the `UpdateTrigger` of the Binding to `OnPropertyChanged` in xaml? Also was the class inherting `INotifyPropertyChanged` happens more so. – Silvermind Oct 23 '12 at 22:22

2 Answers2

4

Create a ToUpper EventTrigger as mentioned here. Also create another one for whatever otherfunctionality you're trying to accomplish. Add them both in xaml:

<TextBox Text="{Binding SomeName, Mode=TwoWay}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TextChanged">
            <myBehaviors:UpperCaseAction/>
        </i:EventTrigger>
        <i:EventTrigger EventName="TextChanged">
            <myBehaviors:MyOtherAction/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

EDIT: I've fully tested this solution using the following (NO code-behind is involved)

UpperCase Action:

public class UpperCaseAction : TriggerAction<TextBox>
{
    protected override void Invoke(object parameter)
    {
        var selectionStart = AssociatedObject.SelectionStart;
        var selectionLength = AssociatedObject.SelectionLength;
        AssociatedObject.Text = AssociatedObject.Text.ToUpper();
        AssociatedObject.SelectionStart = selectionStart;
        AssociatedObject.SelectionLength = selectionLength;
    }
}

Other Action:

public class OtherAction : TriggerAction<TextBox>
{
    Random test = new Random();

    protected override void Invoke(object parameter)
    {
        AssociatedObject.FontSize = test.Next(9, 13);
    }
}

XAML namespaces (TestSL in this case being the namespace of my test project - use your namespace as appropriate):

xmlns:local="clr-namespace:TestSL"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

XAML TextBox

<Grid x:Name="LayoutRoot" Background="LightGray" Width="300" Height="200">
    <TextBox TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10" Width="100">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="TextChanged">
                <local:UpperCaseAction />
            </i:EventTrigger>
            <i:EventTrigger EventName="TextChanged">
                <local:OtherAction />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
</Grid>
Community
  • 1
  • 1
McAden
  • 13,714
  • 5
  • 37
  • 63
  • OK, I was doing something wrong before but I have called multiple event triggers now. Using multiple triggers still results in the same problem if I keep using the KeyUp event. If I switch to using the TextChanged I have problems (no error thrown, but the entered text disappears) since I believe that only works without MVVM (the solution you pointed to uses a codebehind, not a View and ViewModel). – Mario Oct 23 '12 at 22:30
  • The link I sent you to shows code-behind if that's the way you want to do it, but the link displays a MVVM example. – McAden Oct 23 '12 at 22:34
  • Wow, thanks for all the work! I edited my question to show the simplest solution of my problem but I like this better as it is more generic and will probably be easier for people to follow who look at this in the future – Mario Oct 23 '12 at 23:30
  • weird, where did that other guy's answer go? Its like he never commented or posted an answer – Mario Oct 23 '12 at 23:42
1

UpperCaseConverter.cs:

namespace MyProject.Converters
    {
        /// <summary>
        /// A upper case converter for string values.
        /// </summary>
        public class UpperCaseConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return ConvertToUpper(value);
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return ConvertToUpper(value);
            }

            private string ConvertToUpper(object value)
            {
                if (value != null)
                {
                    return value.ToString().ToUpper();
                }
                return null;
            }
        }
    }

AppResources.xaml:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:conv="clr-namespace:MyProject.Converters;assembly=MyProject"
    mc:Ignorable="d"
    >

    <!-- Converters -->
    <conv:UpperCaseConverter x:Key="UpperCaseConverter"/>

</ResourceDictionary>

MyFormView.xaml:

<UserControl>
    <TextBox Text="{Binding myText, Mode=TwoWay, Converter={StaticResource UpperCaseConverter}}" />
</UserControl>
ADM-IT
  • 3,719
  • 1
  • 25
  • 26