/* * Effect - An effect changes a property of a Sprite over time * * Author: Phillip Piper * Date: 23/10/2009 10:29 PM * * Change log: * 2010-03-18 JPP - Added GenericEffect * 2010-03-01 JPP - Added Repeater effect * 2010-02-02 JPP - Added RectangleWalkEffect * 2009-10-23 JPP - Initial version * * To do: * * Copyright (C) 2009 Phillip Piper * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com. */ using System; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Reflection; namespace BrightIdeasSoftware { /// /// An IEffect describes anything that can made a change to a sprite /// over time. /// public interface IEffect { /// /// Gets or set the sprite to which the effect will be applied /// ISprite Sprite { get; set; } /// /// Signal that this effect is about to applied to its sprite for the first time /// void Start(); /// /// Apply this effect to the underlying sprite /// /// How far through the total effect are we? /// This will always in the range 0.0 .. 1.0. void Apply(float fractionDone); /// /// The effect has completed /// void Stop(); /// /// Reset the effect AND the sprite to its condition before the effect was applied. /// void Reset(); } /// /// An Effect provides a do-nothing implementation of the IEffect interface, /// plus providing a number of utility methods. /// public class Effect : IEffect { #region IEffect interface public ISprite Sprite { get { return sprite; } set { sprite = value; } } private ISprite sprite; public virtual void Start() { } public virtual void Apply(float fractionDone) { } public virtual void Stop() { } public virtual void Reset() { } #endregion #region Calculations protected Rectangle CalculateIntermediary(Rectangle from, Rectangle to, float fractionDone) { if (fractionDone >= 1.0f) return to; if (fractionDone <= 0.0f) return from; return new Rectangle( this.CalculateIntermediary(from.X, to.X, fractionDone), this.CalculateIntermediary(from.Y, to.Y, fractionDone), this.CalculateIntermediary(from.Width, to.Width, fractionDone), this.CalculateIntermediary(from.Height, to.Height, fractionDone)); } protected RectangleF CalculateIntermediary(RectangleF from, RectangleF to, float fractionDone) { if (fractionDone >= 1.0f) return to; if (fractionDone <= 0.0f) return from; return new RectangleF( this.CalculateIntermediary(from.X, to.X, fractionDone), this.CalculateIntermediary(from.Y, to.Y, fractionDone), this.CalculateIntermediary(from.Width, to.Width, fractionDone), this.CalculateIntermediary(from.Height, to.Height, fractionDone)); } protected Point CalculateIntermediary(Point from, Point to, float fractionDone) { return new Point( this.CalculateIntermediary(from.X, to.X, fractionDone), this.CalculateIntermediary(from.Y, to.Y, fractionDone)); } protected PointF CalculateIntermediary(PointF from, PointF to, float fractionDone) { return new PointF( this.CalculateIntermediary(from.X, to.X, fractionDone), this.CalculateIntermediary(from.Y, to.Y, fractionDone)); } protected Size CalculateIntermediary(Size from, Size to, float fractionDone) { return new Size( this.CalculateIntermediary(from.Width, to.Width, fractionDone), this.CalculateIntermediary(from.Height, to.Height, fractionDone)); } protected SizeF CalculateIntermediary(SizeF from, SizeF to, float fractionDone) { return new SizeF( this.CalculateIntermediary(from.Width, to.Width, fractionDone), this.CalculateIntermediary(from.Height, to.Height, fractionDone)); } protected char CalculateIntermediary(char from, char to, float fractionDone) { int intermediary = this.CalculateIntermediary(Convert.ToInt32(from), Convert.ToInt32(to), fractionDone); return Convert.ToChar(intermediary); } protected int CalculateIntermediary(int from, int to, float fractionDone) { if (fractionDone >= 1.0f) return to; if (fractionDone <= 0.0f) return from; return from + (int)((to - from) * fractionDone); } protected long CalculateIntermediary(long from, long to, float fractionDone) { if (fractionDone >= 1.0f) return to; if (fractionDone <= 0.0f) return from; return from + (int)((to - from) * fractionDone); } protected float CalculateIntermediary(float from, float to, float fractionDone) { if (fractionDone >= 1.0f) return to; if (fractionDone <= 0.0f) return from; return from + ((to - from) * fractionDone); } protected double CalculateIntermediary(double from, double to, float fractionDone) { if (fractionDone >= 1.0f) return to; if (fractionDone <= 0.0f) return from; return from + ((to - from) * fractionDone); } protected Color CalculateIntermediary(Color from, Color to, float fractionDone) { if (fractionDone >= 1.0f) return to; if (fractionDone <= 0.0f) return from; // There are a couple of different strategies we could use here: // - Calc intermediary individual RGB components - fastest // - Calc intermediary HSB components - nicest results, but slower //Color c = Color.FromArgb( // this.CalculateIntermediary(from.R, to.R, fractionDone), // this.CalculateIntermediary(from.G, to.G, fractionDone), // this.CalculateIntermediary(from.B, to.B, fractionDone) //); Color c = FromHSB( this.CalculateIntermediary(from.GetHue(), to.GetHue(), fractionDone), this.CalculateIntermediary(from.GetSaturation(), to.GetSaturation(), fractionDone), this.CalculateIntermediary(from.GetBrightness(), to.GetBrightness(), fractionDone) ); return Color.FromArgb(this.CalculateIntermediary(from.A, to.A, fractionDone), c); } /// /// Convert a HSB tuple into a RGB color /// /// /// /// /// /// /// Adapted from http://discuss.fogcreek.com/dotnetquestions/default.asp?cmd=show&ixPost=846 /// protected static Color FromHSB(float hue, float saturation, float brightness) { if (hue < 0 || hue > 360) throw new ArgumentException("Value must be between 0 and 360", "hue"); if (saturation < 0 || saturation > 100) throw new ArgumentException("Value must be between 0 and 100", "saturation"); if (brightness < 0 || brightness > 100) throw new ArgumentException("Value must be between 0 and 100", "brightness"); float h = hue; float s = saturation; float v = brightness; int i; float f, p, q, t; float r, g, b; if (saturation == 0) { // achromatic (grey) return Color.FromArgb((int)(v * 255), (int)(v * 255), (int)(v * 255)); } h /= 60; // sector 0 to 5 i = (int)Math.Floor(h); f = h - i; p = v * (1 - s); q = v * (1 - s * f); t = v * (1 - s * (1 - f)); switch (i) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: default: r = v; g = p; b = q; break; } return Color.FromArgb((int)(r * 255f), (int)(g * 255f), (int)(b * 255f)); } protected string CalculateIntermediary(string from, string to, float fractionDone) { int length1 = from.Length; int length2 = to.Length; int length = Math.Max(length1, length2); char[] result = new char[length]; int complete = this.CalculateIntermediary(0, length2, fractionDone); for (int i = 0; i < length; ++i) { if (i < complete) { if (i < length2) result[i] = to[i]; else result[i] = ' '; } else { char fromChar = (i < length1) ? from[i] : 'a'; char toChar = (i < length2) ? to[i] : 'a'; if (toChar == ' ') result[i] = ' '; else { int mid = this.CalculateIntermediary( Convert.ToInt32(fromChar), Convert.ToInt32(toChar), fractionDone); // Unless we're finished, make sure that the same chars don't always map // to the same intermediate value. if (fractionDone < 1.0f) mid -= i; char c = Convert.ToChar(mid); if (Char.IsUpper(toChar) && Char.IsLower(c)) c = Char.ToUpper(c); else if (Char.IsLower(toChar) && Char.IsUpper(c)) c = Char.ToLower(c); result[i] = c; } } } return new string(result); } //protected object CalculateIntermediary(object from, object to, float fractionDone) { // throw new NotImplementedException(); //} #endregion #region Initializations protected void InitializeLocator(IPointLocator locator) { if (locator != null && locator.Sprite == null) locator.Sprite = this.Sprite; } protected void InitializeLocator(IRectangleLocator locator) { if (locator != null && locator.Sprite == null) locator.Sprite = this.Sprite; } protected void InitializeEffect(IEffect effect) { if (effect != null && effect.Sprite == null) effect.Sprite = this.Sprite; } #endregion } /// /// This abstract class save and restores the location of its target sprite /// public class LocationEffect : Effect { public override void Start() { this.originalLocation = this.Sprite.Location; } public override void Reset() { this.Sprite.Location = this.originalLocation; } private Point originalLocation; } /// /// This animation moves from sprite from one calculated point to another /// public class MoveEffect : LocationEffect { #region Life and death public MoveEffect(IPointLocator to) { this.To = to; } public MoveEffect(IPointLocator from, IPointLocator to) { this.From = from; this.To = to; } #endregion #region Configuration properties protected IPointLocator From; protected IPointLocator To; #endregion #region Public methods public override void Start() { base.Start(); if (this.From == null) this.From = new FixedPointLocator(this.Sprite.Location); this.InitializeLocator(this.From); this.InitializeLocator(this.To); } public override void Apply(float fractionDone) { this.Sprite.Location = this.CalculateIntermediary(this.From.GetPoint(), this.To.GetPoint(), fractionDone); } #endregion } /// /// This animation moves a sprite to a given point then halts. /// public class GotoEffect : MoveEffect { public GotoEffect(IPointLocator to) : base(to) { } public override void Apply(float fractionDone) { this.Sprite.Location = this.To.GetPoint(); } } /// /// This animation spins a sprite in place /// public class RotationEffect : Effect { #region Life and death public RotationEffect(float from, float to) { this.From = from; this.To = to; } #endregion #region Configuration properties protected float From; protected float To; #endregion #region Public methods public override void Start() { this.originalSpin = this.Sprite.Spin; } public override void Apply(float fractionDone) { this.Sprite.Spin = this.CalculateIntermediary(this.From, this.To, fractionDone); } public override void Reset() { this.Sprite.Spin = this.originalSpin; } #endregion private float originalSpin; } /// /// This animation fades/reveals a sprite by altering its opacity /// public class FadeEffect : Effect { #region Life and death public FadeEffect(float from, float to) { this.From = from; this.To = to; } #endregion #region Configuration properties protected float From; protected float To ; #endregion #region Public methods public override void Start() { this.originalOpacity = this.Sprite.Opacity; } public override void Apply(float fractionDone) { this.Sprite.Opacity = this.CalculateIntermediary(this.From, this.To, fractionDone); } public override void Reset() { this.Sprite.Opacity = this.originalOpacity; } #endregion private float originalOpacity; } /// /// This animation fades a sprite in and out. /// /// /// /// This effect is structured to have a fade in interval, followed by a full visible /// interval, followed by a fade out interval and an invisible interval. The fade in /// and fade out are linear transitions. Any interval can be skipped by passing zero /// for its period. /// /// The intervals are proportions, not absolutes. They are not the milliseconds /// that will be spent in each phase. They are the relative proportions that will be /// spent in each phase. /// To blink more than once, use a Repeater effect. /// /// sprite.Add(0, 1000, new Repeater(4, new BlinkEffect(1, 2, 1, 1))); /// /// public class BlinkEffect : Effect { #region Life and death public BlinkEffect(int fadeIn, int visible, int fadeOut, int invisible) { this.FadeIn = fadeIn; this.Visible = visible; this.FadeOut = fadeOut; this.Invisible = invisible; } #endregion #region Configuration properties protected int FadeIn ; protected int FadeOut ; protected int Visible ; protected int Invisible ; #endregion #region Public methods public override void Start() { this.originalOpacity = this.Sprite.Opacity; } public override void Apply(float fractionDone) { float total = this.FadeIn + this.Visible + this.FadeOut + this.Invisible; // Are we in the invisible phase? if (((this.FadeIn + this.Visible + this.FadeOut) / total) < fractionDone) { this.Sprite.Opacity = 0.0f; return; } // Are we in the fade out phase? float fadeOutStart = (this.FadeIn + this.Visible) / total; if (fadeOutStart < fractionDone) { float progressInPhase = fractionDone - fadeOutStart; this.Sprite.Opacity = 1.0f - (progressInPhase / (this.FadeOut / total)); return; } // Are we in the visible phase? if (((this.FadeIn) / total) < fractionDone) { this.Sprite.Opacity = 1.0f; return; } // We must be in the FadeIn phase? if (this.FadeIn > 0) this.Sprite.Opacity = fractionDone / (this.FadeIn / total); } public override void Reset() { this.Sprite.Opacity = this.originalOpacity; } #endregion private float originalOpacity; } /// /// This animation enlarges or shrinks a sprite. /// public class ScaleEffect : Effect { #region Life and death public ScaleEffect(float from, float to) { this.From = from; this.To = to; } #endregion #region Configuration properties protected float From ; protected float To ; #endregion #region Public methods public override void Start() { this.originalScale = this.Sprite.Scale; } public override void Apply(float fractionDone) { this.Sprite.Scale = this.CalculateIntermediary(this.From, this.To, fractionDone); } public override void Reset() { this.Sprite.Scale = this.originalScale; } #endregion private float originalScale; } /// /// This animation progressively changes the bounds of a sprite /// public class BoundsEffect : Effect { #region Life and death public BoundsEffect(IRectangleLocator to) { this.To = to; } public BoundsEffect(IRectangleLocator from, IRectangleLocator to) { this.From = from; this.To = to; } #endregion #region Configuration properties protected IRectangleLocator From ; protected IRectangleLocator To ; #endregion #region Public methods public override void Start() { this.originalBounds = this.Sprite.Bounds; if (this.From == null) this.From = new FixedRectangleLocator(this.Sprite.Bounds); if (this.To == null) this.To = new FixedRectangleLocator(this.Sprite.Bounds); this.InitializeLocator(this.From); this.InitializeLocator(this.To); } public override void Apply(float fractionDone) { this.Sprite.Bounds = this.CalculateIntermediary(this.From.GetRectangle(), this.To.GetRectangle(), fractionDone); } public override void Reset() { this.Sprite.Bounds = this.originalBounds; } #endregion private Rectangle originalBounds; } /// /// Move the sprite so that it "walks" around the given rectangle /// public class RectangleWalkEffect : LocationEffect { #region Life and death public RectangleWalkEffect() { } public RectangleWalkEffect(IRectangleLocator rectangleLocator) : this(rectangleLocator, null) { } public RectangleWalkEffect(IRectangleLocator rectangleLocator, IPointLocator alignmentPointLocator) : this(rectangleLocator, alignmentPointLocator, WalkDirection.Clockwise, null) { } public RectangleWalkEffect(IRectangleLocator rectangleLocator, IPointLocator alignmentPointLocator, WalkDirection direction) : this(rectangleLocator, alignmentPointLocator, direction, null) { } public RectangleWalkEffect(IRectangleLocator rectangleLocator, IPointLocator alignmentPointLocator, WalkDirection direction, IPointLocator startPoint) { this.RectangleLocator = rectangleLocator; this.AlignmentPointLocator = alignmentPointLocator; this.WalkDirection = direction; this.StartPointLocator = startPoint; } #endregion #region Configuration properties /// /// Gets or sets the rectangle around which the sprite will be walked /// protected IRectangleLocator RectangleLocator ; /// /// Gets or sets the locator which will return the point on /// rectangle where the "walk" will begin. /// /// If this is null, the walk will start in the top left. protected IPointLocator StartPointLocator ; /// /// Get or set the locator which will return the point of the sprite /// that will be walked around the rectangle /// protected IPointLocator AlignmentPointLocator ; /// /// Gets or sets in what direction the walk will proceed /// protected WalkDirection WalkDirection ; #endregion #region Public methods public override void Start() { base.Start(); if (this.AlignmentPointLocator == null) this.AlignmentPointLocator = Locators.SpriteBoundsPoint(Corner.MiddleCenter); this.InitializeLocator(this.RectangleLocator); this.InitializeLocator(this.AlignmentPointLocator); this.InitializeLocator(this.StartPointLocator); IPointLocator startLocator = this.StartPointLocator ?? Locators.At(this.RectangleLocator.GetRectangle().Location); this.walker = new RectangleWalker(this.RectangleLocator, this.WalkDirection, startLocator); this.walker.Sprite = this.Sprite; this.spriteLocator = new AlignedSpriteLocator(this.walker, this.AlignmentPointLocator); this.spriteLocator.Sprite = this.Sprite; } private RectangleWalker walker; private AlignedSpriteLocator spriteLocator; public override void Apply(float fractionDone) { this.walker.WalkProgress = fractionDone; this.Sprite.Location = this.spriteLocator.GetPoint(); } #endregion } /// /// Move the sprite so that it "walks" along a given series of points /// public class PointWalkEffect : LocationEffect { #region Life and death public PointWalkEffect() { } public PointWalkEffect(IEnumerable points) { this.PointWalker = new PointWalker(points); } public PointWalkEffect(IEnumerable locators) { this.PointWalker = new PointWalker(locators); } #endregion #region Configuration properties protected PointWalker PointWalker; /// /// Get or set the locator which will return the point of the sprite /// that will be walked around the rectangle /// protected IPointLocator AlignmentPointLocator ; #endregion #region Public methods public override void Start() { base.Start(); if (this.AlignmentPointLocator == null) this.AlignmentPointLocator = Locators.SpriteBoundsPoint(Corner.MiddleCenter); this.InitializeLocator(this.PointWalker); this.InitializeLocator(this.AlignmentPointLocator); this.spriteLocator = new AlignedSpriteLocator(this.PointWalker, this.AlignmentPointLocator); this.spriteLocator.Sprite = this.Sprite; } private AlignedSpriteLocator spriteLocator; public override void Apply(float fractionDone) { this.PointWalker.WalkProgress = fractionDone; this.Sprite.Location = this.spriteLocator.GetPoint(); } #endregion } /// /// A TickerBoard clicks through from on text string to another string in a way /// vaguely similar to old style airport tickerboard displays. /// /// /// This is not a particularly useful class :) Someone might find it helpful. /// public class TickerBoardEffect : Effect { public TickerBoardEffect() { } public TickerBoardEffect(string endString) { this.EndString = endString; } protected string StartString ; public string EndString ; protected TextSprite TextSprite { get { return (TextSprite)this.Sprite; } } public override void Start() { System.Diagnostics.Debug.Assert(this.Sprite is TextSprite); if (String.IsNullOrEmpty(this.StartString)) this.StartString = this.TextSprite.Text; } public override void Apply(float fractionDone) { this.TextSprite.Text = this.CalculateIntermediary(this.StartString, this.EndString, fractionDone); } public override void Reset() { this.TextSprite.Text = this.StartString; } } /// /// Repeaters operate on another Effect and cause it to repeat one or more times. /// /// /// It does *not* change the duration of the effect. It divides the original effects duration /// into multiple parts. /// /// /// animation.Add(0, 1000, new Repeater(4, Effect.Move(Corner.TopLeft, Corner.BottomRight))); /// public class Repeater : Effect { public Repeater() { } public Repeater(int repetitions, IEffect effect) { this.Repetitions = repetitions; this.Effect = effect; } public int Repetitions ; public IEffect Effect ; public override void Start() { this.InitializeEffect(this.Effect); } public override void Apply(float fractionDone) { if (fractionDone >= 1.0f) this.Effect.Apply(1.0f); else { // Calculate how far we are through this repetition double x = fractionDone * this.Repetitions; double newFractionDone = x - Math.Truncate(x); // get just the fractional part this.Effect.Apply((float)newFractionDone); } } } /// /// A GenericEffect allows any property to be set. The /// public class GenericEffect : Effect { #region Life and death public GenericEffect() { munger = new SimpleMunger(""); } public GenericEffect(string propertyName) { this.PropertyName = propertyName; } public GenericEffect(string propertyName, T from, T to) { this.PropertyName = propertyName; this.From = from; this.To = to; } #endregion #region Public properties /// /// Get or set the property that will be changed by this Effect /// public string PropertyName { get { return propertyName; } set { propertyName = value; munger = new SimpleMunger(value); } } private string propertyName; /// /// Gets or set the starting value for this effect /// public object From ; /// /// Gets or sets the ending value /// public object To ; #endregion #region IEffect interface public override void Start() { this.originalValue = this.GetValue(); } public override void Apply(float fractionDone) { object intermediateValue = default(T); // Until we use 'dynamic' in C# 4.0, there is no way to force the compiler to // resolve which CalculateIntermediate we want based on T. So we have to put // this ugly switch statement here. (Yes, I know we could initialize a Dictionary, // keyed by Type and hold the function, but that's just more complication for no real payback). if (typeof(T) == typeof(int)) intermediateValue = this.CalculateIntermediary((int)this.From, (int)this.To, fractionDone); else if (typeof(T) == typeof(float)) intermediateValue = this.CalculateIntermediary((float)this.From, (float)this.To, fractionDone); else if (typeof(T) == typeof(long)) intermediateValue = this.CalculateIntermediary((long)this.From, (long)this.To, fractionDone); else if (typeof(T) == typeof(Point)) intermediateValue = this.CalculateIntermediary((Point)this.From, (Point)this.To, fractionDone); else if (typeof(T) == typeof(Rectangle)) intermediateValue = this.CalculateIntermediary((Rectangle)this.From, (Rectangle)this.To, fractionDone); else if (typeof(T) == typeof(Size)) intermediateValue = this.CalculateIntermediary((Size)this.From, (Size)this.To, fractionDone); else if (typeof(T) == typeof(PointF)) intermediateValue = this.CalculateIntermediary((PointF)this.From, (PointF)this.To, fractionDone); else if (typeof(T) == typeof(RectangleF)) intermediateValue = this.CalculateIntermediary((RectangleF)this.From, (RectangleF)this.To, fractionDone); else if (typeof(T) == typeof(SizeF)) intermediateValue = this.CalculateIntermediary((SizeF)this.From, (SizeF)this.To, fractionDone); else if (typeof(T) == typeof(char)) intermediateValue = this.CalculateIntermediary((char)this.From, (char)this.To, fractionDone); else if (typeof(T) == typeof(string)) intermediateValue = this.CalculateIntermediary((string)this.From, (string)this.To, fractionDone); else if (typeof(T) == typeof(Color)) intermediateValue = this.CalculateIntermediary((Color)this.From, (Color)this.To, fractionDone); else System.Diagnostics.Debug.Assert(false, "Unknown data type"); this.SetValue(intermediateValue); } public override void Stop() { this.SetValue(this.originalValue); } #endregion #region Implementation /// /// Gets the value of our named property from our sprite /// /// protected virtual T GetValue() { return this.munger.GetValue(this.Sprite); } protected virtual void SetValue(object value) { this.munger.PutValue(this.Sprite, value); } #endregion #region Implementation variable protected T originalValue; protected SimpleMunger munger; #endregion /// /// Instances of this class know how to peek and poke values from an object /// using reflection. /// /// /// This is a simplified form of the Munger from the ObjectListView project /// (http://objectlistview.sourceforge.net). /// protected class SimpleMunger { public SimpleMunger() { } public SimpleMunger(String memberName) { this.MemberName = memberName; } /// /// The name of the aspect that is to be peeked or poked. /// /// /// /// This name can be a field, property or parameter-less method. /// public string MemberName { get { return memberName; } set { memberName = value; } } private string memberName; /// /// Extract the value indicated by our MemberName from the given target. /// /// The object that will be peeked /// The value read from the target public U GetValue(Object target) { if (String.IsNullOrEmpty(this.MemberName)) return default(U); const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.GetProperty | BindingFlags.GetField; // We specifically don't try to catch errors here. If this don't work, it's // a programmer error that needs to be fixed. return (U)target.GetType().InvokeMember(this.MemberName, flags, null, target, null); } /// /// Poke the given value into the given target indicated by our MemberName. /// /// The object that will be poked /// The value that will be poked into the target public void PutValue(Object target, object value) { if (String.IsNullOrEmpty(this.MemberName)) return; if (target == null) return; try { // Try to set a property or field first, since that's the most common case const BindingFlags flags3 = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.SetField; target.GetType().InvokeMember(this.MemberName, flags3, null, target, new Object[] { value }); } catch (System.MissingMethodException) { // If that failed, it could be method name that we are looking for. // We specifically don't catch errors here. const BindingFlags flags4 = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod; target.GetType().InvokeMember(this.MemberName, flags4, null, target, new Object[] { value }); } } } } }