private void setProperty(object sender, PropertyUpdateArgs args) { try { if (isDisposed(args.target)) { return; } var target = args.target as ISynchronizeInvoke; if (target != null && target.InvokeRequired) { target.BeginInvoke(new EventHandler <PropertyUpdateArgs>(setProperty), new object[2] { sender, args }).AsyncWaitHandle.WaitOne(50); } else { args.propertyInfo.SetValue(args.target, args.value, null); } } catch (Exception ex) { } }
/// <summary> /// Sets a property on the object passed in to the value passed in. This method /// invokes itself on the GUI thread if the property is being invoked on a GUI /// object. /// </summary> private void setProperty(object sender, PropertyUpdateArgs args) { try { // If the target is a control that has been disposed then we don't // try to update its proeprties. This can happen if the control is // on a form that has been closed while the transition is running... if (isDisposed(args.target)) { return; } ISynchronizeInvoke invokeTarget = args.target as ISynchronizeInvoke; if (invokeTarget != null && invokeTarget.InvokeRequired) { // There is some history behind the next two lines, which is worth // going through to understand why they are the way they are. // Initially we used BeginInvoke without the subsequent WaitOne for // the result. A transition could involve a large number of updates // to a property, and as this call was asynchronous it would send a // large number of updates to the UI thread. These would queue up at // the GUI thread and mean that the UI could be some way behind where // the transition was. // The line was then changed to the blocking Invoke call instead. This // meant that the transition only proceded at the pace that the GUI // could process it, and the UI was not overloaded with "old" updates. // However, in some circumstances Invoke could block and lock up the // Transitions background thread. In particular, this can happen if the // control that we are trying to update is in the process of being // disposed - for example, it is on a form that is being closed. See // here for details: // http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/7d2c941a-0016-431a-abba-67c5d5dac6a5 // To solve this, we use a combination of the two earlier approaches. // We use BeginInvoke as this does not block and lock up, even if the // underlying object is being disposed. But we do want to wait to give // the UI a chance to process the update. So what we do is to do the // asynchronous BeginInvoke, but then wait (with a short timeout) for // it to complete. IAsyncResult asyncResult = invokeTarget.BeginInvoke(new EventHandler <PropertyUpdateArgs>(setProperty), new[] { sender, args }); asyncResult.AsyncWaitHandle.WaitOne(50); } else { // We are on the correct thread, so we update the property... args.propertyInfo.SetValue(args.target, args.value, null); } } catch (Exception) { // We silently catch any exceptions. These could be things like // bounds exceptions when setting properties. } }
/// <summary> /// Called when the transition timer ticks. /// </summary> internal void onTimer() { // When the timer ticks we: // a. Find the elapsed time since the transition started. // b. Work out the percentage movement for the properties we're managing. // c. Find the actual values of each property, and set them. // a. int iElapsedTime = (int)m_Stopwatch.ElapsedMilliseconds; // b. double dPercentage; bool bCompleted; m_TransitionMethod.onTimer(iElapsedTime, out dPercentage, out bCompleted); // We take a copy of the list of properties we are transitioning, as // they can be changed by another thread while this method is running... IList <TransitionedPropertyInfo> listTransitionedProperties = new List <TransitionedPropertyInfo>(); lock (m_Lock) { foreach (TransitionedPropertyInfo info in m_listTransitionedProperties) { listTransitionedProperties.Add(info.copy()); } } // c. bool propLeft = false; foreach (TransitionedPropertyInfo info in listTransitionedProperties) { // We get the current value for this property... object value = info.managedType.getIntermediateValue(info.startValue, info.endValue, dPercentage); // We set it... PropertyUpdateArgs args = new PropertyUpdateArgs(info.target, info.propertyInfo, value); setProperty(this, args); propLeft = true; } bCompleted = bCompleted && propLeft; // Has the transition completed? if (bCompleted) { // We stop the stopwatch and the timer... m_Stopwatch.Stop(); // We raise an event to notify any observers that the transition has completed... Utility.raiseEvent(TransitionCompletedEvent, this, new Args()); } }
/// <summary> /// Sets a property on the object passed in to the value passed in. This method /// invokes itself on the GUI thread if the property is being invoked on a GUI /// object. /// </summary> private void setProperty(object sender, PropertyUpdateArgs args) { try { // If the target is a control that has been disposed then we don't // try to update its properties. This can happen if the control is // on a form that has been closed while the transition is running... if (isDisposed(args.target) == true) { return; } ISynchronizeInvoke invokeTarget = args.target as ISynchronizeInvoke; if (invokeTarget != null && invokeTarget.InvokeRequired) { // There is some history behind the next two lines, which is worth // going through to understand why they are the way they are. // Initially we used BeginInvoke without the subsequent WaitOne for // the result. A transition could involve a large number of updates // to a property, and as this call was asynchronous it would send a // large number of updates to the UI thread. These would queue up at // the GUI thread and mean that the UI could be some way behind where // the transition was. // The line was then changed to the blocking Invoke call instead. This // meant that the transition only proceded at the pace that the GUI // could process it, and the UI was not overloaded with "old" updates. // However, in some circumstances Invoke could block and lock up the // Transitions background thread. In particular, this can happen if the // control that we are trying to update is in the process of being // disposed - for example, it is on a form that is being closed. See // here for details: // http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/7d2c941a-0016-431a-abba-67c5d5dac6a5 // To solve this, we use a combination of the two earlier approaches. // We use BeginInvoke as this does not block and lock up, even if the // underlying object is being disposed. But we do want to wait to give // the UI a chance to process the update. So what we do is to do the // asynchronous BeginInvoke, but then wait (with a short timeout) for // it to complete. IAsyncResult asyncResult = invokeTarget.BeginInvoke(new EventHandler<PropertyUpdateArgs>(setProperty), new object[] { sender, args }); asyncResult.AsyncWaitHandle.WaitOne(50); } else { // We are on the correct thread, so we update the property... args.propertyInfo.SetValue(args.target, args.value, null); } } catch (Exception) { // We silently catch any exceptions. These could be things like // bounds exceptions when setting properties. } }
/// <summary> /// Called when the transition timer ticks. /// </summary> internal void onTimer() { // When the timer ticks we: // a. Find the elapsed time since the transition started. // b. Work out the percentage movement for the properties we're managing. // c. Find the actual values of each property, and set them. // a. int iElapsedTime = (int)m_Stopwatch.ElapsedMilliseconds; // b. double dPercentage; bool bCompleted; m_TransitionMethod.onTimer(iElapsedTime, out dPercentage, out bCompleted); // We take a copy of the list of properties we are transitioning, as // they can be changed by another thread while this method is running... IList<TransitionedPropertyInfo> listTransitionedProperties = new List<TransitionedPropertyInfo>(); lock (m_Lock) { foreach (TransitionedPropertyInfo info in m_listTransitionedProperties) { listTransitionedProperties.Add(info.copy()); } } // c. foreach (TransitionedPropertyInfo info in listTransitionedProperties) { // We get the current value for this property... object value = info.managedType.getIntermediateValue(info.startValue, info.endValue, dPercentage); // We set it... PropertyUpdateArgs args = new PropertyUpdateArgs(info.target, info.propertyInfo, value); setProperty(this, args); } // Has the transition completed? if (bCompleted == true) { // We stop the stopwatch and the timer... m_Stopwatch.Stop(); // We raise an event to notify any observers that the transition has completed... Utility.raiseEvent(TransitionCompletedEvent, this, new Args()); } }