0
votes

I am creating a custom UWP user control. I am two way binding the text in a TextBox with an IValueConverter to convert a string to decimal and back. My converter is converting the decimal to a string in the Textbox when loading the data from behind. However, I am unable to trigger my converter's ConvertBack method when the Textbox loses focus inside my custom control like the usual behavior of a regular Textbox control.

Is there a way to notice the TextBox unfocus and trigger the conversion back to the decimal inside my usercontrol back to my model data though its dependency property ?

Here is my UserControl.

<UserControl
    <Grid Margin="15,0,15,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5*"/>
            <ColumnDefinition Width="5*"/>
        </Grid.ColumnDefinitions>
        <TextBox
            x:Name="textBox"
            Grid.Row="0"
            Grid.Column="0"
            Grid.ColumnSpan="2"
            Header="{x:Bind Header}"
            Text="{x:Bind Text}"
            IsEnabled="{x:Bind IsControlEnabled}"/>

        <TextBlock
            Grid.Row="1" 
            Grid.Column="0" 
            Foreground="RosyBrown"
            Text="{x:Bind InspectionValidation}"/>
    </Grid>
</UserControl>

Here is it's code-behind

public sealed partial class FrmTextBox : UserControl, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(string),
            typeof(FrmComboBox), new PropertyMetadata(null));

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string),
            typeof(FrmComboBox), new PropertyMetadata(null));

    public static readonly DependencyProperty
        InspectionValidationProperty =
            DependencyProperty.Register("InspectionValidation", typeof(string)
                , typeof(FrmInspection), null);

    public static readonly DependencyProperty
        IsLayoutEnabledProperty =
            DependencyProperty.Register("IsLayoutEnabled", typeof(int?)
                , typeof(FrmInspection), null);


    public string Header
    {
        get { return (string)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set
        {
            SetValue(TextProperty, value);
        }
    }

    public string InspectionValidation
    {
        get => (string) GetValue(InspectionValidationProperty);
        set =>SetValue( InspectionValidationProperty, value);
    }

    public int? IsLayoutEnabled
    {
        get => (int?) GetValue(IsLayoutEnabledProperty);
        set
        {
            IsControlEnabled = (value == -1) ? true : false;
            SetValue( IsLayoutEnabledProperty, value);
        }
    }
    private bool isControlEnabled { get; set; }
    public bool IsControlEnabled
    {
        get => isControlEnabled;
        set
        {
            if (value == isControlEnabled) return;
            isControlEnabled = value;
            OnPropertyChanged();

        }
    }

    public FrmTextBox()
    {
        this.InitializeComponent();
    }

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        // Raise the PropertyChanged event, passing the name of the property whose value has changed.
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Here is my IValueConverter

public class StringToDecimalConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value != null)
            return value.ToString();

        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        try
        {
            if (value != null)
            {
                Decimal newValue;
                Decimal.TryParse((string) value, out newValue);
                return newValue;
            }
            return null;
        }
        catch
        {
            return null;
        }
    }
}


This is my Xaml implementation of the user control.

<controls:FrmTextBox
   Grid.Row="1"
   Grid.Column="1"
   Header="Labor Count"
   Text="{x:Bind ViewModel.Current.LaborCount, Mode=TwoWay, Converter={StaticResource StringToDecimal}}"
   InspectionValidation=""
   IsLayoutEnabled="-1">
</controls:FrmTextBox>
1

1 Answers

1
votes

Is there a way to notice the TextBox unfocus and trigger the conversion back to the decimal inside my usercontrol back to my model data though its dependency property ?

You can subscribe the LostFocus event to listen if the TextBox is unfocus. Then in the LostFocus event, you can pass the current value of textbox to your Text dependency property, when the value of Text property changes, it will trigger the conversion back method and then back the decimal to your model data.

In your usercontrol.xaml:

<TextBox
        x:Name="textBox"
        Grid.Row="0"
        Grid.Column="0"
        Grid.ColumnSpan="2"
        Header="{x:Bind Header}"
        Text="{x:Bind Text}"
        IsEnabled="{x:Bind IsControlEnabled}" LostFocus="textBox_LostFocus"/>

In your usercontrol.cs:

......

private void textBox_LostFocus(object sender, RoutedEventArgs e)
{
    Text = textBox.Text;
}

Or directly set the binding model of your textbox as Twoway.

In your usercontrol.xaml:

<TextBox
        x:Name="textBox"
        Grid.Row="0"
        Grid.Column="0"
        Grid.ColumnSpan="2"
        Header="{x:Bind Header}"
        Text="{x:Bind Text,Mode=TwoWay}"
        IsEnabled="{x:Bind IsControlEnabled}"/>