【问题标题】:BindingOperations.GetBinding give another bindingBindingOperations.GetBinding 给出另一个绑定
【发布时间】:2019-01-18 15:41:23
【问题描述】:

我在 FooBox(升级的文本框)中动态使用 ValidationRule。 直接在窗口中使用时效果很好。

我有另一个自定义控件 (LatLonEditor) 来使用 FooBox 管理纬度和经度。 在这种特殊情况下,当我获得 FooBox 值的绑定时,我总是获得第一个 LatLonEditor 的第一个 FooBox 的绑定。

我花了一天时间试图解决这个问题,但我的解决方案已经用完了。 我已经阅读了 BindingOperations.GetBinding 源代码(没有给我任何线索)。 我创建了一个独立的项目来重现剥离代码中所有无用部分的问题。

FooBox 模板

<ControlTemplate x:Key="FooBoxTemplate" TargetType="{x:Type local:FooBox}">
    <TextBox x:Name="Editor" Text="{Binding Path=Value, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,1,0" />

    <ControlTemplate.Triggers>            
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
                                Path=(Validation.Errors).CurrentItem.ErrorContent}"/>                
            <Setter Property="BorderBrush" TargetName="Editor" Value="Red" />                
        </Trigger>            
    </ControlTemplate.Triggers>
</ControlTemplate>

<Style x:Key="DefaultFooBoxStyle" TargetType="{x:Type local:FooBox}">
    <Setter Property="Template" Value="{StaticResource FooBoxTemplate}"/>
    <Setter Property="Height" Value="24" />             
    <Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
    <Setter Property="Focusable" Value="False" />
</Style>

<Style TargetType="{x:Type local:FooBox}" BasedOn="{StaticResource DefaultFooBoxStyle}" />

FooBox 控件

 public class FooBox : Control
{
    static FooBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(FooBox), new FrameworkPropertyMetadata(typeof(FooBox)));
    }

    public enum Type
    {          
        Int,          
        Float,           
        Double,            
        String
    }

    private bool _templateApplied = false;
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        _templateApplied = true;
        LoadValidationRules();
    }

    public static readonly DependencyProperty ValueProperty =
   DependencyProperty.Register("Value", typeof(string), typeof(FooBox), new FrameworkPropertyMetadata()
   {
       BindsTwoWayByDefault = true,
       DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,

   });

    public string Value
    {
        get { return (string)GetValue(ValueProperty); }
        set
        {
            SetValue(ValueProperty, value);
        }
    }

    public static readonly DependencyProperty ValueTypeProperty =
     DependencyProperty.Register("ValueType", typeof(Type), typeof(FooBox), new FrameworkPropertyMetadata()
     {
         DefaultValue = Type.String
     });

    public Type ValueType
    {
        get { return (Type)GetValue(ValueTypeProperty); }
        set
        {
            SetValue(ValueTypeProperty, value);
        }
    }        

    /// <summary>
    /// For integral types, this is the max acceptable value
    /// </summary>
    public static readonly DependencyProperty DomainMaxProperty =
       DependencyProperty.Register("DomainMax", typeof(decimal?), typeof(FooBox), new FrameworkPropertyMetadata());
    public decimal? DomainMax
    {
        get { return (decimal?)GetValue(DomainMaxProperty); }
        set
        {
            SetValue(DomainMaxProperty, value);
        }
    }

    private void LoadValidationRules()
    {
        if (_templateApplied)
        {
            //For the second LatLonEditor, i've got the binding of the previous one
            Binding b = BindingOperations.GetBinding(this, ValueProperty);

            if (b != null)
            {
                b.ValidationRules.Clear();

                if (ValueType == Type.Double)
                {
                    b.ValidationRules.Add(new DoubleValidationRule()
                    {                          
                        DomainMax = DomainMax
                    });
                }
            }
        }
    }
}

LatLonEditor 模板

 <ControlTemplate x:Key="LatLonDecimalDegreesEditorTemplate" TargetType="{x:Type local:LatLonEditor}">                                  
    <local:FooBox x:Name="PART_DD" ValueType="Double" Margin="2,0"
                    Value="{Binding Value, Delay=500, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged,                                       
                            RelativeSource={RelativeSource TemplatedParent}}"/>          

    <ControlTemplate.Triggers>
        <Trigger Property="Type" Value="Latitude">
            <Setter TargetName="PART_DD" Property="DomainMax" Value="90" />
        </Trigger>
        <Trigger Property="Type" Value="Longitude">
            <Setter TargetName="PART_DD" Property="DomainMax" Value="180" />
        </Trigger>          
    </ControlTemplate.Triggers>
</ControlTemplate>    

<Style x:Key="DefaultLatLonEditorStyle" TargetType="{x:Type local:LatLonEditor}">
    <Setter Property="Template" Value="{StaticResource LatLonDecimalDegreesEditorTemplate}"/>        
</Style>

<Style BasedOn="{StaticResource DefaultLatLonEditorStyle}" TargetType="{x:Type local:LatLonEditor}" />

LatLonEditor 控件

public class LatLonEditor : Control
{
    #region Statements            


    #endregion

    #region Constructor/Destructor

    static LatLonEditor()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(LatLonEditor), new FrameworkPropertyMetadata(typeof(LatLonEditor)));
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
    }     

    #endregion

    /// <summary>
    /// The types of value that can be input
    /// </summary>
    public enum CoordinateValueType : byte
    {
        Latitude,
        Longitude
    }

    #region Properties

    /// <summary>
    /// Get/Set the input mode for this instance
    /// </summary>
    public static readonly DependencyProperty TypeProperty =
        DependencyProperty.Register("Type", typeof(CoordinateValueType), typeof(LatLonEditor), new FrameworkPropertyMetadata()
        {
            DefaultValue = CoordinateValueType.Latitude
        });
    public CoordinateValueType Type
    {
        get { return (CoordinateValueType)GetValue(TypeProperty); }
        set { SetValue(TypeProperty, value); }
    }

    /// <summary>
    /// Formatted value to use externally
    /// </summary>
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(double?), typeof(LatLonEditor), new FrameworkPropertyMetadata());
    public double? Value
    {
        get
        {
            return (double?)GetValue(ValueProperty);
        }
        set
        {
            SetValue(ValueProperty, value);
        }
    }
    #endregion
}

用法

 <controls:LatLonEditor x:Name="H3LP" Width="120"  Type="Longitude" Value="3" />
    <controls:LatLonEditor x:Name="IfYouPlease" Width="120" Type="Latitude" Value="5" />

第一个 LatLonEditor 的最大值应为 180 第二个 LatLonEditor 的最大值应为 90

DomainMax 由触发器正确设置。

实际结果是两个控件的最大值都是90 (第一个绑定应用第二个控件的规则)

我当然错过了一些东西,但我没有看到什么? :-(

【问题讨论】:

  • 我可以提供一个示例项目来重现问题-_-

标签: .net wpf xaml


【解决方案1】:

您应该首先获得对已应用绑定的元素的引用,然后使用BindingOperations.GetBinding

private void LoadValidationRules()
{
    if (_templateApplied)
    {
        TextBox Editor = Template.FindName("Editor", this) as TextBox;
        Binding b = BindingOperations.GetBinding(Editor, TextBox.TextProperty);
        if (b != null)
        {
            ...
        }
    }
}

由于不应修改 Binding,您还可以通过编程方式创建初始绑定:

private void LoadValidationRules()
{
    if (_templateApplied)
    {
        TextBox Editor = Template.FindName("Editor", this) as TextBox;
        Binding b = new Binding(nameof(Value)) { Source = this };
        if (ValueType == Type.Double)
        {
            b.ValidationRules.Add(new DoubleValidationRule()
            {
                //DomainMax = DomainMax
            });
        }
        BindingOperations.SetBinding(Editor, TextBox.TextProperty, b);

    }
}

那么你肯定会得到每个实例的唯一绑定。

【讨论】:

  • 你好mm8,我试过你的提议,结果完全一样。对于第二个编辑器,绑定已经有一个 ValidationRule。我试图将绑定存储在列表中,它们是相同的参考。 _bindedStorage[0] == _bindedStorage[1] 为真!这让我发疯:(
  • @OKcowboy:请提供a minimal repo of your issue
  • 问题中贴出的代码足以重现问题。为了获得一些时间,我重新创建了一个最小的项目,我在这里上传:ufile.io/1et0w 感谢您的帮助!
  • @OKcowboy:您可以通过编程方式创建绑定。查看我的编辑。
  • 动态实例化绑定,确实给我唯一的绑定。感谢您解决此问题。知道为什么 GetBinding 似乎没有完成他的工作吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
  • 2021-08-22
  • 1970-01-01
  • 2020-08-25
  • 1970-01-01
相关资源
最近更新 更多