Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I change the properties of a binding in a DataTrigger, without knowing the binding itself?

I have a TextBox style that formats a number if the box is unfocused, but leaves the number unformatted whlie it's being edited.

This is the style I want for multiple number TextBoxes, but they all contain different Text bindings. The only difference between the regular Text setter and the Triggered Text setter is that the Triggered one has StringFormat=N2 in the binding.

Is there a way I can make this style generic, such as only changing the StringFormat property of the binding in the DataTrigger?

<TextBox>
    <TextBox.Style>
        <Style TargetType="{x:Type TextBox}">
                <Setter Property="Text" Value="{Binding SomeValue, StringFormat=N2, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
            <Style.Triggers>
                <Trigger Property="IsKeyboardFocusWithin" Value="True">
                    <Setter Property="Text" Value="{Binding SomeValue, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>
like image 755
Rachel Avatar asked Feb 03 '12 13:02

Rachel


3 Answers

Is there a way I can make this style generic, such as only changing the StringFormat property of the binding in the DataTrigger?

Inherit Style and new XAML would become this:

 <TextBox>
    <TextBox.Style>
        <local:FlyingStyle Binding="{Binding ElementName=This, Path=SomeValue}" StringFormat="F2" />
    </TextBox.Style>
 </TextBox>

Here's the class...

public class FlyingStyle : Style
{
    public FlyingStyle()
        : base(typeof(TextBox))
    { }

    string _stringFormat;
    public string StringFormat
    {
        get { return _stringFormat; }
        set
        {
            _stringFormat = value;
            CheckInitialize();
        }
    }
    Binding _binding;
    public Binding Binding
    {
        get { return _binding; }
        set
        {
            _binding = value;
            CheckInitialize();
        }
    }
    void CheckInitialize()
    {
        if (StringFormat == null || Binding == null) { return; }// need both

        Setters.Add(CreateSetter(Binding, StringFormat));

        var trigger = new Trigger
        {
            Property = UIElement.IsKeyboardFocusWithinProperty,
            Value = true,
        };
        trigger.Setters.Add(CreateSetter(Binding));
        Triggers.Add(trigger);
    }

    /// <summary>Creates the common <see cref="Setter"/>.</summary>
    static Setter CreateSetter(Binding binding, string stringFormat = null)
    {
        // must create a copy, because same binding ref but diff StringFormats
        var bindingCopy = new Binding
        {
            // these could be copies as well
            UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
            ValidatesOnDataErrors = true,
            Mode = BindingMode.TwoWay,
            Path = binding.Path,

            AsyncState = binding.AsyncState,
            BindingGroupName = binding.BindingGroupName,
            BindsDirectlyToSource = binding.BindsDirectlyToSource,
            Converter = binding.Converter,
            ConverterCulture = binding.ConverterCulture,
            ConverterParameter = binding.ConverterParameter,
            ElementName = binding.ElementName,
            FallbackValue = binding.FallbackValue,
            IsAsync = binding.IsAsync,
            NotifyOnSourceUpdated = binding.NotifyOnSourceUpdated,
            NotifyOnTargetUpdated = binding.NotifyOnTargetUpdated,
            NotifyOnValidationError = binding.NotifyOnValidationError,
            //StringFormat = set below...
            TargetNullValue = binding.TargetNullValue,
            UpdateSourceExceptionFilter = binding.UpdateSourceExceptionFilter,
            ValidatesOnExceptions = binding.ValidatesOnExceptions,
            XPath = binding.XPath,
            //ValidationRules = binding.ValidationRules
        };
        // mutex ElementName, so modify if needed
        // Source = binding.Source,
        // RelativeSource = binding.RelativeSource,

        if (stringFormat != null)
        {
            bindingCopy.StringFormat = stringFormat;
        }
        return new Setter(TextBox.TextProperty, bindingCopy);
    }
}

Note that my test was

  • the generic MainWindow
  • impl INotifyPropertyChanged
  • SomeValue INPC property
  • DataContext = this
  • x:Name = This
like image 68
Jake Berger Avatar answered Nov 14 '22 17:11

Jake Berger


The only option I see there would be to create an attached property for the StringFormat and use a multiBinding.

Not quite what you wanted, but close enough, I guess...

you have more info on this (kind of duplicate) question on S.O.:

Modifying the Parameters of a TextBox's Text Binding through the use of a Style

like image 38
David Avatar answered Nov 14 '22 18:11

David


My attempt to solve this ended up with a custom control plus a multi-binding, both of which where suggested above.

This allowed me to use markup like this:

< CustomTextBox Value="{Binding Value"} Format="N2" />

I tried to post my code but I keep getting an error about "code that is not properly formatted as code".

like image 30
Phil Avatar answered Nov 14 '22 18:11

Phil