BindingOperations.GetBinding дает другую привязку - PullRequest
0 голосов
/ 18 января 2019

Я использую ValidationRule динамически в FooBox (обновленное текстовое поле). Он работает нормально, когда используется непосредственно в окне.

У меня есть другой пользовательский элемент управления (LatLonEditor) для управления широтой и долготой с помощью FooBox. В этом конкретном случае, когда я получаю привязку значения FooBox, я всегда получаю привязку первого FooBox первого LatLonEditor.

Я провел день, пытаясь это исправить, и у меня заканчиваются решения. Я прочитал исходный код 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 (При первом связывании применяется правило второго элемента управления)

Я, конечно, что-то упускаю, но я не вижу что? : - (

1 Ответ

0 голосов
/ 18 января 2019

Сначала вы должны получить ссылку на элемент, к которому вы применили привязку, а затем использовать 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);

    }
}

Тогда вы непременно получите уникальную привязку для каждого экземпляра.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...