【问题标题】:Can I change the properties of a binding in a DataTrigger, without knowing the binding itself?我可以在不知道绑定本身的情况下更改 DataTrigger 中绑定的属性吗?
【发布时间】:2012-02-03 13:09:16
【问题描述】:

我有一个 TextBox 样式,如果框没有聚焦,它会格式化一个数字,但在编辑它时会留下未格式化的数字。

这是我想要的多个文本框的样式,但它们都包含不同的文本绑定。常规 Text setter 和 Triggered Text setter 之间的唯一区别是 Triggered 的绑定中有StringFormat=N2

有没有办法可以使这种样式通用,例如只更改 DataTrigger 中绑定的StringFormat 属性?

<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>

【问题讨论】:

  • 我认为绑定的任何属性在应用后都无法更改。至少当我尝试更改现有绑定的 StringFormat 属性时,我得到一个 InvalidOperationException 说 Binding cannot be changed after it has been used. 在我看来,每次焦点更改时都无法创建新绑定。
  • 再次考虑这一点,并考虑了某种附加的Data 属性解决方案,其中属性更改处理程序将附加 GotFocus/LostFocus 处理程序等。最后我更希望有一个派生的 TextBox属性DataStringFormat,将根据它们的值和当前焦点状态设置Text 属性。

标签: wpf xaml data-binding styles


【解决方案1】:

有没有办法让这种风格通用,比如只改变 DataTrigger 中绑定的 StringFormat 属性?

继承 Style 并且新的 XAML 将变为:

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

这是课程...

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);
    }
}

注意我的测试是

  • 通用主窗口
  • impl INotifyPropertyChanged
  • SomeValueINPC财产
  • DataContext = 这个
  • x:Name = 这个

【讨论】:

  • 我希望将我的样式保留在 XAML 中,以便我可以轻松访问它以对其进行更改。这看起来像是一场噩梦:)
  • This is the style I want for multiple number TextBoxes, but they all contain different Text bindings 听起来像是值得使用 C#。无论如何,大卫答案中的链接提出了几乎相同的算法。
  • +1 用于工作版本,虽然我更喜欢 David 的链接中显示的附加属性,因为它允许我在 XAML 中保留我的其余样式
【解决方案2】:

我看到的唯一选择是为 StringFormat 创建一个附加属性并使用 multiBinding。

不是你想要的,但足够接近,我猜......

你有更多关于 S.O. 的(重复的)问题的信息:

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

【讨论】:

    【解决方案3】:

    我解决这个问题的尝试以自定义控件和多重绑定结束,这两者都在上面建议。

    这让我可以像这样使用标记:

    我试图发布我的代码,但我不断收到关于“代码格式不正确的代码”的错误。

    【讨论】:

      【解决方案4】:

      我想知道您是否可以在编辑上附加一个属性来保存格式化值(仅绑定到应用了 Stringformat 的实际编辑值),然后在失焦触发器中设置编辑值到这个属性。

      这会导致循环绑定,但当编辑没有焦点时,不知道 WPF 在这种情况下如何反应。

      【讨论】:

        【解决方案5】:

        遗憾的是(据我所知)这是不可能的。一种可能的解决方法是以编程方式动态创建这样的样式,可以将其封装在MarkupExtension 中,该MarkupExtension 将路径作为构造函数参数。

        【讨论】:

          猜你喜欢
          • 2013-09-05
          • 1970-01-01
          • 2010-09-08
          • 2016-05-04
          • 2013-06-18
          • 2012-01-31
          • 1970-01-01
          • 2013-06-25
          • 1970-01-01
          相关资源
          最近更新 更多