繁体   English   中英

绑定延迟一步

[英]Binding delay one step

好的,让我们看看我是否可以用英语很好地解释这一点。 这会很长,对不起,请耐心等待,我不知道如何缩短它。

我有两个实体作为 canvas 的UserControl的子级,它们的拇指分别是UserControl的子级。 我有一条通过拇指连接这些实体的折线,该折线是同一 canvas 的子代。

拇指有一个称为AnchorPoint (拇指的中心)的依赖属性,折线的起点和终点通过各自的StartEnd依赖属性绑定到该属性。

当我移动实体时,我以编程方式将AnchorPoint属性更新到所需的点(拇指位置的中心),绑定会更改线点属性,当属性更改时,我会在折线上调用InvalidateMeasure 因此线随着实体移动。

和代码(我省略了不相关的代码):

UserControl的连接拇指,孩子:

public partial class EntityConnectingThumb : UserControl, IChartEntityConnectingThumb, IObjectWithCancellableAction, IChartObjectCanBeRemoved, INotifyPropertyChanged
{
    internal List<LineConnection> _LinesStarts = new List<LineConnection>();
    internal List<LineConnection> _LinesEnds = new List<LineConnection>();

    public Point AnchorPoint
    {
        get { return (Point)GetValue(AnchorPointProperty); }
        private set { SetValue(AnchorPointProperty, value); }
    }
    public static readonly DependencyProperty AnchorPointProperty =
        DependencyProperty.Register("AnchorPoint", typeof(Point), typeof(EntityConnectingThumb), new PropertyMetadata(default(Point)));

    private void UpdateAnchorPoint()
    {
        if(_LinesStarts.Count > 0 || _LinesEnds.Count > 0)
            Console.WriteLine("Update anchor point");

        Size size = RenderSize;
        Point ofs = new Point(size.Width / 2, size.Height / 2);

        if (_LinesStarts.Count > 0 || _LinesEnds.Count > 0)
            Console.WriteLine($"ofs = {ofs} {Environment.NewLine}Pre anchorpoint = {this.TranslatePoint(ofs, ChartCustomControl.Instance.ChartCanvas)}");

        AnchorPoint = this.TranslatePoint(ofs, ChartCustomControl.Instance.ChartCanvas); 

        if (_LinesStarts.Count > 0 || _LinesEnds.Count > 0)
            Console.WriteLine($"AnchorPoint= {AnchorPoint}");
    }
    public void MyBorderMovedTo()
    {
        if (_LinesStarts.Count > 0 || _LinesEnds.Count > 0)
            Console.WriteLine("My border moved to");
        UpdateAnchorPoint();
    }
}

行 class,Canvas 的孩子( aChartArrowLineBase实现Shape ):

public class LineConnection : aChartArrowLineBase, IChartObjectCanBeRemoved, IChartHaveHiddableThumbs
{
    public ObservableCollection<aLineSegmentBase> Segments
    {
        get { return (ObservableCollection<aLineSegmentBase>)GetValue(SegmentsProperty); }
        set { SetValue(SegmentsProperty, value); }
    }
    public static readonly DependencyProperty SegmentsProperty =
        DependencyProperty.Register("Segments", typeof(ObservableCollection<aLineSegmentBase>), typeof(LineConnection), new FrameworkPropertyMetadata(
            null, FrameworkPropertyMetadataOptions.AffectsMeasure, SegmentsPropertyChanged));

    public new void InvalidateMeasure()
    {
        base.InvalidateMeasure();
        _HitboxPolyLine.InvalidateMeasure();
    }
    /// <summary>
    ///     Gets a value that represents the Geometry of the ArrowPolyline.
    /// </summary>
    protected override Geometry DefiningGeometry
    {
        get
        {
            // Clear out the PathGeometry.
            _Pathgeo.Figures.Clear();
            _PathfigLine.Segments.Clear();
            _HitboxPolyLine.Points.Clear();

            // Try to avoid unnecessary indexing exceptions.
            if (Segments.Count > 0)
            {
                // Define a PathFigure containing the points.
                _PathfigLine.StartPoint = Segments[0].Start;
                _PolysegLine.Points.Clear();

                for (int i = 0; i < Segments.Count; i++)
                {
                    var current = Segments[i];
                    switch(current._Type)
                    {
                        case LineSegmentTypesEnum.Start:
                        case LineSegmentTypesEnum.Normal:
                            _PolysegLine.Points.Add(current.Start);
                            _HitboxPolyLine.Points.Add(current.Start);
                            break;
                        case LineSegmentTypesEnum.Unique:
                        case LineSegmentTypesEnum.End:
                            _PolysegLine.Points.Add(current.Start);
                            _PolysegLine.Points.Add(current.End);
                            _HitboxPolyLine.Points.Add(current.Start);
                            _HitboxPolyLine.Points.Add(current.End);
                            break;
                    }
                }

                _PathfigLine.Segments.Add(_PolysegLine);
                _Pathgeo.Figures.Add(_PathfigLine);
            }

            // Call the base property to add arrows on the ends.
            return base.DefiningGeometry;
        }
    }
}

段基础 class, LineConnection的孩子,这里是StartEnd属性:

public abstract class aLineSegmentBase : FrameworkElement, IChartHaveHiddableThumbs, ILineSegmentBase
{
    public aLineSegmentBase(LineSegmentTypesEnum Type, LineConnection connection, EntityConnectingThumb start, EntityConnectingThumb end)
    {
        Start = start.AnchorPoint;
        End = end.AnchorPoint;
        var b = new Binding()
        {
            Source = start,
            Path = new PropertyPath(EntityConnectingThumb.AnchorPointProperty),
            Mode = BindingMode.TwoWay,
            UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        };
        SetBinding(StartProperty, b);
        b = new Binding()
        {
            Source = end,
            Path = new PropertyPath(EntityConnectingThumb.AnchorPointProperty),
            Mode = BindingMode.TwoWay,
            UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        };
        SetBinding(EndProperty, b);
        _Loaded = true;
    }
    public Point Start
    {
        get { return (Point)GetValue(StartProperty); }
        set { SetValue(StartProperty, value); }
    }
    public static readonly DependencyProperty StartProperty =
        DependencyProperty.Register("Start", typeof(Point), typeof(aLineSegmentBase), new PropertyMetadata(default(Point), OnPointPropChanged));
    public Point End
    {
        get { return (Point)GetValue(EndProperty); }
        set { SetValue(EndProperty, value); }
    }
    public static readonly DependencyProperty EndProperty =
        DependencyProperty.Register("End", typeof(Point), typeof(aLineSegmentBase), new PropertyMetadata(default(Point), OnPointPropChanged));
    private static void OnPointPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((aLineSegmentBase)d).OnPointChanged();
    }
    private void OnPointChanged()
    {
        Console.WriteLine($"Line start changed= {p}");
        if (!_Loaded)
            return;
        _Connection.InvalidateMeasure();
    }
}

正如您所看到的, DefiningGeometry迭代使用StartEnd属性构建折线的线段,这些属性通过绑定链接到拇指锚点。

当锚点改变时,线点发生改变,在线上调用InvalidateMeasure ,重绘。

当我移动UserControls时,我调用每个拇指UpdateAnchorPoint方法并且一切正常,线条移动没有问题:

private void MovingThumb_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        //Calculating new point p ...

        Canvas.SetLeft(this, p.X - _MovingThumbPositionWhenClickedRelativeToCanvas.X);
        Canvas.SetTop(this, p.Y - _MovingThumbPositionWhenClickedRelativeToCanvas.Y);

        UpdateAnchorPoint();
        CalculateSnapCoords(); //Here all thumb's UpdateAnchorPoint methods are called

        ///other stuff...

        e.Handled = true;
    }
internal void CalculateSnapCoords()
    {
        //Doing snap stuff...

        DoActionAllConnectingThumbs(x => x.MyBorderMovedTo());

        //More snap stuff...
    }

但是现在我正在实现撤消/重做的东西,为此我使用此方法将UserControl移动到它们的上一个(撤消)位置或下一个(重做)位置:

internal void AutomaticMoveTo(Point p)
    {
        Console.WriteLine($"Automatic move to = {p}");
        Canvas.SetLeft(this, p.X);
        Canvas.SetTop(this, p.Y);
        UpdateAnchorPoint();
        CalculateSnapCoords();
    }

现在问题来了:这适用于实体,但行更新为“延迟一步”,我解释说:

1.-我按下撤消按钮,实体移动到上一个位置,线根本没有反应。

2.-我按下重做按钮,实体移动正确,但线移动到它应该移动的位置 1.-

3.- 再次撤消按钮,实体移动正确,线移动到 2.- 位置。

... 等等。

所以,绑定延迟了一步……??? 老实说,我是一个业余爱好者,但我什至不知道这是可能的。

我对此感到困惑,在我看来,绑定在值更改之前到达目标,因此新值不会传递给目标。 因此,当下一步发生时,目标取旧值而不是新值,并且新值存储在属性中而不传递给目标。

阅读控制台 output 似乎这正是正在发生的事情:

//---------------------------------------------------------------Last message moving entity
My border moved to
Update anchor point
ofs = 7,5;5 
Pre anchorpoint = 660,80198019802;464,212871287129
AnchorPoint= 660,80198019802;464,212871287129
* Creating undo/redo command:_UndoMovingCoordinates = 609,39603960396;118,594059405941
  ;  Last coordinates = 643,30198019802;442,212871287129
//---------------------------------------------------------------Here start undo/redo
*** UNDO move to = 609,39603960396;118,594059405941
Automatic move to = 609,39603960396;118,594059405941
My border moved to
Update anchor point
ofs = 7,5;5 
Pre anchorpoint = 660,80198019802;464,212871287129
//---------------------------------------------------------------Here lacks a line, see below
AnchorPoint= 660,80198019802;464,212871287129
*** REDO move to = 643,30198019802;442,212871287129
Automatic move to = 643,30198019802;442,212871287129
My border moved to
Update anchor point
ofs = 7,5;5 
Pre anchorpoint = 626,89603960396;140,594059405941
Line start changed= 626,89603960396;140,594059405941
AnchorPoint= 626,89603960396;140,594059405941
*** UNDO move to = 609,39603960396;118,594059405941
Automatic move to = 609,39603960396;118,594059405941
My border moved to
Update anchor point
ofs = 7,5;5 
Pre anchorpoint = 660,80198019802;464,212871287129
Line start changed= 660,80198019802;464,212871287129
AnchorPoint= 660,80198019802;464,212871287129

您可以看到Line start changed=行没有出现在第一条撤消消息中:第一次撤消更改不会更新Start属性(或者至少没有启动属性更改事件)。 它保持了当时rest的一步延迟。

我尝试在StartEnd属性绑定上使用UpdateSourceUpdateTarget ,并在AnchorPoint属性上使用PropertyChanged ,但没有任何变化。

我不能将AnchorPoint属性直接绑定到连接拇指Canvas.LeftPropertyCanvas.TopProperty因为连接拇指不是UserControl的孩子

我不知道发生了什么。 想法,提示,一些东西,都是受欢迎的。

我找到了解决方法。

问题是在折线DefiningGeometry中绘制折线的点是每个线段的StartEnd 这些点绑定到它们各自拇指的AnchorPoint属性。

当实体移动AnchorPoint属性时,会像这样以编程方式更新...

private void UpdateAnchorPoint()
{
    Size size = RenderSize;
    Point ofs = new Point(size.Width / 2, size.Height / 2);
    AnchorPoint = this.TranslatePoint(ofs, ChartCustomControl.Instance.ChartCanvas);
}

...锚点发生变化,起点/终点发生变化, PropertyChangedCallback在线上调用InvalidateMeasure并且线移动。

这在拖动实体时非常有效,但在仅通过Canvas.SetLeftCanvas.SetTop更改实体位置一次时不起作用,这就是撤消/重做命令的作用:

internal void AutomaticMoveTo(Point p)
        {
            Canvas.SetLeft(this, p.X);
            Canvas.SetTop(this, p.Y);
            UpdateAnchorPoint();
            CalculateSnapCoords();
        }

由于某种原因,当实体的位置以这种方式更改时,开始/结束属性的PropertyChangedCallback不会第一次被调用(不要问我,我不知道为什么),因此累积了“一步延迟”。

但是,如果我手动计算拇指中心并分配一个新创建的Point object...

public void AutomaticMoveUpdateAnchorPoint()
        {
            double x = Canvas.GetLeft(MyBorder);
            double y = Canvas.GetTop(MyBorder);
            switch (Name)
            {
                case "ThTop":
                    x += (MyBorder.BorderContent.ActualWidth * 0.5d) + (MyBorder.RootGrid.ColumnDefinitions[0].ActualWidth * 1.75d);
                    y += (MyBorder.RootGrid.RowDefinitions[0].ActualHeight * 0.5d);
                    break;
                case "ThBottom":
                    x += (MyBorder.BorderContent.ActualWidth * 0.5d) + (MyBorder.RootGrid.ColumnDefinitions[0].ActualWidth * 1.75d);
                    y += MyBorder.BorderContent.ActualHeight + (MyBorder.RootGrid.RowDefinitions[0].ActualHeight * 1.5d);
                    break;
                case "ThLeft":
                    x += (MyBorder.RootGrid.ColumnDefinitions[0].ActualWidth);
                    y += (MyBorder.BorderContent.ActualHeight * 0.5d) + (MyBorder.RootGrid.RowDefinitions[0].ActualHeight);
                    break;
                case "ThRight":
                    x += (MyBorder.RootGrid.ColumnDefinitions[1].ActualWidth) + (MyBorder.RootGrid.ColumnDefinitions[0].ActualWidth * 2.25d);
                    y += (MyBorder.BorderContent.ActualHeight * 0.5d) + (MyBorder.RootGrid.RowDefinitions[0].ActualHeight);
                    break;
            }

            AnchorPoint = new Point(x, y);
        }

...它再次完美运行。 绑定再次正常工作,并且再次正常调用PropertyChangedCallback

我不知道为什么会这样。 绑定是标准的TwoWayUpdateSourceTrigger.PropertyChanged ,我知道最初的TranslatePoint行也创建了一个新的Point object。 也许它没有反映 canvas 的变化? 我不知道,我不明白为什么会发生这种情况,但是,我有解决方法,我使用新方法,一切正常。

我会把它留在这里,以防有人可以使用它,甚至找到原因。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM