Search code examples
xamlwindows-phone-8windows-runtimewinrt-xamlc++-cx

How to bind a Button's IsEnabled member to a TextBox's content?


I have a fairly standard setup: Based on whether the contents of a TextBox are empty, I want to enable/disable a Button control.

I tried, what would have worked in a WPF application, but that doesn't produce the desired behavior when running on the Windows Runtime:

<StackPanel>
    <TextBox x:Name="ItemNameTextBox" Header="Item" />
    <Button Content="Add Item"
            IsEnabled="{Binding ElementName=ItemNameTextBox,
                                Path=Text.Length,
                                Mode=OneWay}" />
</StackPanel>

At runtime, however, this produces the following debug output1):

Error: BindingExpression path error: 'Length' property not found on 'Windows.Foundation.IReference`1<String>'.

The error message is correct: Windows::Foundation::IReference<String> doesn't have a Length property.

Question: How do I bind the IsEnabled property of the Button to the TextBoxs text length property? Do I have to implement a custom data converter, or can this be done using markup only?


1) Full error output:

Error: BindingExpression path error: 'Length' property not found on 'Windows.Foundation.IReference`1<String>'. BindingExpression: Path='Text.Length' DataItem='Windows.UI.Xaml.Controls.TextBox'; target element is 'Windows.UI.Xaml.Controls.Button' (Name='null'); target property is 'IsEnabled' (type 'Boolean')

Solution

  • You can't bind a boolean to an integer.

    You need to create a converter that returns a boolean whose value is true or false depending on the value of the integer. In your case I assume that you want a length of zero to return false and a length of greater than zero to return true. If in c++/cx Length isn't a property but a method (unlike C#) then you're not going to be able to bind to it directly. You are going to have to bind the the Text property and then call the Length method in the converter:

    public ref class LengthToIsEnabledConverter sealed : public Windows::UI::Xaml::Data::IValueConverter
    {
    public:
        virtual Platform::Object^ Convert( Platform::Object^ value,
                                           Windows::UI::Xaml::Interop::TypeName targetType,
                                           Platform::Object^ parameter,
                                           Platform::String^ language ) {
            Platform::String^ text = safe_cast<Platform::String^>( value );
            return ( text->Length() > 0 );
        }
    
        virtual Platform::Object^ ConvertBack( Platform::Object^ value,
                                               Windows::UI::Xaml::Interop::TypeName targetType,
                                               Platform::Object^ parameter,
                                               Platform::String^ language ) {
            throw ref new Platform::NotImplementedException();
        }
    };
    

    Then in your binding you add the converter and change the path to be just the Text:

    IsEnabled="{Binding ElementName=ItemNameTextBox,
                        Path=Text,
                        Mode=OneWay,
                        Converter={StaticResource LengthToIsEnabledConverter}" />
    

    where the converter is declared in XAML like this:

    <UserControl.Resources>
        <converters:LengthToIsEnabledConverter x:Key="LengthToIsEnabledConverter " />
    </UserControl.Resources>