How to avoid recursive triggering of events in WPF?

I am having two WPF (from the standard set) widgets A and B. When I change some property of A it should be set on B, when it is change in B it should be set on A.

Now I have this ugly recursion --> I change A, so code changes B, but since B is changed, it changes A, so it changes B... You have the picture.

How to avoid this recursion the most "standard" way? Naive deleting and adding event handlers does not work, and checking if the new value is the same as old value is not applicable here (because of the fluctuation of calculation -- I am not setting the same value to A and B, but transformed).

Background

I always try to put minimum info about the problem to avoid confusion. However, this might help

  • I didn't write those widgets, I just handle the events, that's all
  • despite the title "recursive triggering", the handlers are called sequentially, so you have the sequence entry-exit-entry-exit-entry-exit, not entry-entry-entry-exit-exit-exit

    and the last, probably the least important, but nevertheless

  • in this particular case I have common handler for A and B
  • A and B (in this case) are scrollviewers and I try to maintain proportionally the same position for both of them. The project (by Karin Huber) is here: http://www.codeproject.com/KB/WPF/ScrollSynchronization.aspx

    Event triggering

    The idea of blocking the events is so popular that I added the sequence of triggering the events, here we go:

  • I change the A
  • A handler is called
  • I disable handler of A
  • I change B (this is stored, but not triggered)
  • I enable handler of A
  • now the event is get from the queue
  • B handler is called
  • I disable handler of B
  • I change A
  • ...
  • As you see, this is futile.


    Rather than raising events, refactor your code so that the event handlers for A and B call another method to do the actual work.

    private void EventHandlerA(object sender, EventArgs e)
    {
        ChangeA();
        ChangeB();
    }
    
    private void EventHandlerB(object sender, EventArgs e)
    {
        ChangeB();
        ChangeA();
    }
    

    You could then extend/change these methods if you need to do subtly different things if changing A directly or via B.

    UPDATE

    Given that you can't change/don't have access to the code this isn't the solution.


    First of all, I would think about the design because those circular dependencies are often a sign of bad design.

    However, there might be situations where such dependencies are the only way to go. In these case, I would suggest to use private flags indicating whether a change in B was caused by a change in A. Something like this ( updated ):

    public class A
    {
        private bool m_ignoreChangesInB = false;
    
        private void B_ChangeOccurred(object sender, EventArgs e)
        {
            if (!m_ignoreChangesInB)
            {
                // handle the changes...
            }
        }
    
        private void SomeMethodThatChangesB()
        {
            m_ignoreChangesInB = true;
            // perform changes in B...
            m_ignoreChangesInB = false;
        }
    }
    

    The same approach should be used in class B. However, this approach does not handle changes from multiple threads. If A or B might be changed from multiple threads at the same time, you will have to use appropriate techniques to avoid that property changes are lost.


    ChrisFs solution is probably the way to go, but sometimes we remove the event, make the change, and re-add the event handler:

    Imagine a DataContext, with the DataContextChanged event:

    DataContextChanged -= OnDataContextChanged;
    DataContext = new object();
    DataContextChanged += OnDataContextChanged;
    

    This way, you will know for sure your event handler will not go off so to speak. The event still fires however ;).

    链接地址: http://www.djcxy.com/p/47518.html

    上一篇: 为什么接口在动态/松散

    下一篇: 如何避免WPF中事件的递归触发?