posts - 644, comments - 2003, trackbacks - 137

My Links

News

Raffaele Rialdi website

Su questo sito si trovano i miei articoli, esempi, snippet, tools, etc.

Archives

Post Categories

Image Galleries

Blogs

Links

WPF dimentica ISynchronizeInvoke

ISynchronizeInvoke è l'interfaccia del namespace System.ComponentModel che viene implementata da Windows.Forms.Control (e tutte le classi derivate) per "imbucare" un messaggio (richiesta di esecuzione di un delegate) al thread principale (quello della form). In questo modo il delegate viene eseguito nel contesto del thread principale che è l'unico a poter accedere alla UI.

Tra le cose che adoro nel codice ci sono le astrazioni e l'incapsulamento. E così spesso mi sono trovato a incapsulare la logica di invocazione di ISinchronizeInvoke all'interno della classe o componente che necessita di thread secondari.

In questi giorni mi trovavo a scrivere una nuova UI WPF che attinge i dati da un frame grabber video di cui una mia classe esegue la decodifica dello stream proveniente dai socket. La prima versione della classe chiamava ISinchronizeInvoke.Invoke per essere disaccoppiata da Winform. Quando l'avevo scritta mi ero detto ... un giorno anche la prossima generazione di UI implementerà ISinchronizeInvoke e tutto sarà già pronto.

Scopro invece che è tutto diverso, nessuna interfaccia comune tra la Invoke delle Winform e la DispatcherObject.Invoke di WPF. Anche i parametri vanno passati in modo differente.

L'unica soluzione per tenere disaccoppiata questa classe dalle dipendenze di Winform e WPF è quella di un bel (?) wrapperone con reflection. Soluzione elegante per una implementazione certamente non elegante.

Peccato WPF, questa è un occasione persa. Se qualche altro maniaco dell'ordine nel codice ne avesse bisogno, ecco la prima versione... testata ma come sempre "as is" smile_regular. Bug feedback is always welcome!

   1: /// <summary>
   2: /// Class to wrap Winform and WPF Invoke
   3: /// The problem is that WPF doesn't implement ISynchronizeInvoke
   4: /// </summary>
   5: internal class InvokeWrapper
   6: {
   7:     TargetType _TargetType;
   8:     object _InvokeTargetObject;
   9:     Type _TypeToInvoke;
  10:     Delegate _Method;
  11:     object _Priority;
  12:  
  13:     private enum TargetType
  14:     {
  15:         WPF,
  16:         Winform,
  17:         Unknown
  18:     }
  19:  
  20:     public InvokeWrapper(object InvokeTargetObject, Delegate Method)
  21:     {
  22:         _InvokeTargetObject = InvokeTargetObject;
  23:         _TypeToInvoke = _InvokeTargetObject.GetType();
  24:         this._Method = Method;
  25:  
  26:         _TargetType = TargetType.Unknown;
  27:         while(_TypeToInvoke.BaseType != typeof(object))
  28:         {
  29:             if(_TypeToInvoke.BaseType.FullName == "System.Windows.Forms.Form")
  30:             {
  31:                 _TargetType = TargetType.Winform;
  32:                 break;
  33:             }
  34:             if(_TypeToInvoke.BaseType.FullName == "System.Windows.Threading.DispatcherObject")
  35:             {
  36:                 _TargetType = TargetType.WPF;
  37:  
  38:                 string Qualified = "System.Windows.Threading.DispatcherPriority, WindowsBase, " +
  39:                     "Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
  40:                 // obtained by
  41:                 // System.Reflection.Assembly.CreateQualifiedName("WindowsBase",
  42:                 //        "System.Windows.Threading.DispatcherPriority");
  43:  
  44:                 Type Pri = Type.GetType(Qualified);
  45:                 _Priority = Enum.ToObject(Pri, 9);
  46:                 // 9 ==> System.Windows.Threading.DispatcherPriority.Normal
  47:  
  48:                 object Disp = _TypeToInvoke.InvokeMember("Dispatcher",
  49:                     System.Reflection.BindingFlags.Instance |
  50:                     System.Reflection.BindingFlags.GetProperty |
  51:                     System.Reflection.BindingFlags.Public,
  52:                     null,
  53:                     _InvokeTargetObject,
  54:                     new object[] { });
  55:                 _InvokeTargetObject = Disp;
  56:                 _TypeToInvoke = Disp.GetType();
  57:                 break;
  58:             }
  59:             _TypeToInvoke = _TypeToInvoke.BaseType;
  60:         }
  61:  
  62:     }
  63:  
  64:     /// <summary>
  65:     /// Invoke the delegate
  66:     /// </summary>
  67:     /// <returns></returns>
  68:     public object Invoke()
  69:     {
  70:         return Invoke(null);
  71:     }
  72:  
  73:     /// <summary>
  74:     /// Invoke the delegate
  75:     /// </summary>
  76:     /// <param name="Param">The first parameter</param>
  77:     /// <param name="Parameters">the other parameters</param>
  78:     /// <returns></returns>
  79:     public object Invoke(object Param, params object[] Parameters)
  80:     {
  81:         if(Parameters == null || Parameters.Length == 0)
  82:             return Invoke(new object[] { Param });
  83:  
  84:         object[] NewParameters = new object[Parameters.Length + 1];
  85:         NewParameters[0] = Param;
  86:         Array.Copy(Parameters, 0, NewParameters, 1, Parameters.Length);
  87:  
  88:         return Invoke(NewParameters);
  89:     }
  90:  
  91:     /// <summary>
  92:     /// Invoke the delegate
  93:     /// </summary>
  94:     /// <param name="Parameters">Parameters for the delgate or null </param>
  95:     /// <returns>object returned from Invoke</returns>
  96:     public object Invoke(object[] Parameters)
  97:     {
  98:         if(_TargetType == TargetType.Winform)
  99:         {
 100:             object Res;
 101:             object[] InvokeMemberParameters;
 102:  
 103:             if(Parameters == null || Parameters.Length == 0)
 104:                 InvokeMemberParameters = new object[] { _Method };
 105:             else
 106:                 InvokeMemberParameters = new object[] { _Method, Parameters };
 107:  
 108:             Res = _TypeToInvoke.InvokeMember("Invoke",
 109:                 System.Reflection.BindingFlags.Instance |
 110:                 System.Reflection.BindingFlags.InvokeMethod |
 111:                 System.Reflection.BindingFlags.Public,
 112:                 null,
 113:                 _InvokeTargetObject,
 114:                 InvokeMemberParameters);
 115:             return Res;
 116:         }
 117:         if(_TargetType == TargetType.WPF)
 118:         {
 119:             object Res = null;
 120:             object[] InvokeMemberParameters;
 121:  
 122:             if(Parameters == null || Parameters.Length == 0)
 123:                 InvokeMemberParameters = new object[] { _Priority, _Method };
 124:             else
 125:             {
 126:                 if(Parameters.Length == 1)
 127:                     InvokeMemberParameters = new object[]
 128:                         { _Priority, _Method, Parameters[0] };
 129:                 else
 130:                     InvokeMemberParameters = new object[]
 131:                         { _Priority, _Method, Parameters[0], ExtractSecondPartArray(Parameters) };
 132:             }
 133:  
 134:  
 135:             Res = _TypeToInvoke.InvokeMember("Invoke",
 136:                 System.Reflection.BindingFlags.Instance |
 137:                 System.Reflection.BindingFlags.InvokeMethod |
 138:                 System.Reflection.BindingFlags.Public,
 139:                 null,
 140:                 _InvokeTargetObject,
 141:                 InvokeMemberParameters);
 142:  
 143:             return Res;
 144:         }
 145:  
 146:         throw new Exception(_TypeToInvoke.ToString() + " is not supported from this wrapper");
 147:     }
 148:  
 149:     /// <summary>
 150:     /// Given an n-dimension array (0 to n), returns a new n-1 array with 1 to n elements
 151:     /// </summary>
 152:     /// <param name="Params">Source array</param>
 153:     /// <returns>Array stripped of the first element</returns>
 154:     private object[] ExtractSecondPartArray(object[] Params)
 155:     {
 156:         if(Params == null)
 157:             throw new ArgumentNullException();
 158:         if(Params.Length == 0)
 159:             throw new ArgumentException("Source array should contain at least one element");
 160:         if(Params.Length == 1)
 161:             return null;
 162:  
 163:         object[] NewArray = new object[Params.Length - 1];
 164:         Array.Copy(Params, 1, NewArray, 0, NewArray.Length);
 165:         return NewArray;
 166:     }
 167:  
 168: }

(riformattato perché troppo largo)

Print | posted on martedì 22 gennaio 2008 14:47 |

Feedback

Gravatar

# re: WPF dimentica ISynchronizeInvoke

Thanks for this comment
23/08/2018 12:39 | rajan
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET