diff options
| author | j-p <jp.bruyere@live.be> | 2016-03-16 10:41:22 +0000 |
|---|---|---|
| committer | j-p <jp.bruyere@live.be> | 2016-03-16 10:41:22 +0000 |
| commit | 356bfaaed4f46a727ae033f37f362b062c8c32d9 (patch) | |
| tree | 60d62ce23dbf7c1dc8ec5de4f0dcdb77eb66789c /src/GraphicObjects | |
| parent | 6b0d7e5a317f4deb808b59f4393fb8b5d0488587 (diff) | |
| parent | c85cd50c4a6553679ebf92058aa1f609ede56d27 (diff) | |
Merge pull request #5 from jpbruyere/TwoWayBindings
Two way bindings
Diffstat (limited to 'src/GraphicObjects')
| -rw-r--r-- | src/GraphicObjects/GraphicObject.cs | 338 | ||||
| -rw-r--r-- | src/GraphicObjects/IBindable.cs | 31 | ||||
| -rw-r--r-- | src/GraphicObjects/TextBox.cs | 71 |
3 files changed, 54 insertions, 386 deletions
diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index cddc918a..c526e909 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -156,6 +156,7 @@ namespace Crow public event EventHandler<MouseMoveEventArgs> MouseLeave; public event EventHandler<KeyboardKeyEventArgs> KeyDown; public event EventHandler<KeyboardKeyEventArgs> KeyUp; + public event EventHandler<KeyPressEventArgs> KeyPress; public event EventHandler Focused; public event EventHandler Unfocused; public event EventHandler<LayoutingEventArgs> LayoutChanged; @@ -939,6 +940,9 @@ namespace Crow public virtual void onKeyDown(object sender, KeyboardKeyEventArgs e){ KeyDown.Raise (sender, e); } + public virtual void onKeyPress(object sender, KeyPressEventArgs e){ + KeyPress.Raise (sender, e); + } #endregion #region Mouse handling @@ -1055,342 +1059,10 @@ namespace Crow #if DEBUG_BINDING Debug.WriteLine ("Resolve Bindings => " + this.ToString ()); #endif - //grouped bindings by Instance of Source - Dictionary<object,List<Binding>> resolved = new Dictionary<object, List<Binding>>(); - - foreach (Binding b in Bindings) { - if (b.Resolved) - continue; - - if (b.Target.Member.MemberType == MemberTypes.Event) { - if (b.Expression.StartsWith("{")){ - CompileEventSource(b); - continue; - } - if (!b.FindTarget ()) - continue; - //register handler for event - if (b.Source.Method == null) { - Debug.WriteLine ("\tError: Handler Method not found: " + b.ToString()); - continue; - } - try { - MethodInfo addHandler = b.Target.Event.GetAddMethod (); - Delegate del = Delegate.CreateDelegate (b.Target.Event.EventHandlerType, b.Source.Instance, b.Source.Method); - addHandler.Invoke (this, new object[] { del }); - - #if DEBUG_BINDING - Debug.WriteLine ("\tHandler binded => " + b.ToString()); - #endif - b.Resolved = true; - } catch (Exception ex) { - Debug.WriteLine ("\tERROR: " + ex.ToString()); - } - continue; - } - - if (!b.FindTarget ()) - continue; - - List<Binding> bindings = null; - if (!resolved.TryGetValue (b.Source.Instance, out bindings)) { - bindings = new List<Binding> (); - resolved [b.Source.Instance] = bindings; - } - bindings.Add (b); - b.Resolved = true; - - } - - MethodInfo stringEquals = typeof(string).GetMethod - ("Equals", new Type[3] {typeof(string), typeof(string), typeof(StringComparison)}); - Type sourceType = this.GetType(); - EventInfo ei = typeof(IValueChange).GetEvent("ValueChanged"); - MethodInfo evtInvoke = ei.EventHandlerType.GetMethod ("Invoke"); - ParameterInfo[] evtParams = evtInvoke.GetParameters (); - Type handlerArgsType = evtParams [1].ParameterType; - Type[] args = {typeof(object), typeof(object),handlerArgsType}; - FieldInfo fiNewValue = typeof(ValueChangeEventArgs).GetField("NewValue"); - FieldInfo fiMbName = typeof(ValueChangeEventArgs).GetField("MemberName"); - - //group;only one dynMethods by target (valuechanged event source) - //changed value name tested in switch - //IEnumerable<Binding[]> groupedByTarget = resolved.GroupBy (g => g.Target.Instance, g => g, (k, g) => g.ToArray ()); - foreach (List<Binding> grouped in resolved.Values) { - int i = 0; - Type targetType = grouped[0].Source.Instance.GetType(); - - DynamicMethod dm = null; - ILGenerator il = null; - - System.Reflection.Emit.Label[] jumpTable = null; - System.Reflection.Emit.Label endMethod = new System.Reflection.Emit.Label(); - - #region Retrieve EventHandler parameter type - //EventInfo ei = targetType.GetEvent ("ValueChanged"); - //no dynamic update if ValueChanged interface is not implemented - if (targetType.GetInterfaces().Contains(typeof(IValueChange))){ - dm = new DynamicMethod(grouped[0].NewDynMethodId, - MethodAttributes.Family | MethodAttributes.FamANDAssem | MethodAttributes.NewSlot, - CallingConventions.Standard, - typeof(void), - args, - sourceType,true); - - il = dm.GetILGenerator(256); - - endMethod = il.DefineLabel(); - jumpTable = new System.Reflection.Emit.Label[grouped.Count]; - for (i = 0; i < grouped.Count; i++) - jumpTable [i] = il.DefineLabel (); - il.DeclareLocal(typeof(string)); - il.DeclareLocal(typeof(object)); - - il.Emit(OpCodes.Nop); - il.Emit(OpCodes.Ldarg_0); - //il.Emit(OpCodes.Isinst, sourceType); - //push new value onto stack - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldfld, fiNewValue); - il.Emit(OpCodes.Stloc_1); - //push name - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldfld, fiMbName); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Brfalse, endMethod); - } - #endregion - - i = 0; - foreach (Binding b in grouped) { - #region initialize target with actual value - object targetValue = null; - if (b.Source.Member != null){ - if (b.Source.Member.MemberType == MemberTypes.Property) - targetValue = b.Source.Property.GetGetMethod ().Invoke (b.Source.Instance, null); - else if (b.Source.Member.MemberType == MemberTypes.Field) - targetValue = b.Source.Field.GetValue (b.Source.Instance); - else if (b.Source.Member.MemberType == MemberTypes.Method){ - MethodInfo mthSrc = b.Source.Method; - if (mthSrc.IsDefined(typeof(ExtensionAttribute), false)) - targetValue = mthSrc.Invoke(null, new object[] {b.Source.Instance}); - else - targetValue = mthSrc.Invoke(b.Source.Instance, null); - }else - throw new Exception ("unandled source member type for binding"); - }else if (string.IsNullOrEmpty(b.Expression)) - targetValue= grouped [0].Source.Instance;//empty binding exp=> bound to target object by default - //TODO: handle other dest type conversions - if (b.Target.Property.PropertyType == typeof(string)){ - if (targetValue == null){ - //set default value - - }else - targetValue = targetValue.ToString (); - } - if (targetValue != null) - b.Target.Property.GetSetMethod ().Invoke - (this, new object[] { b.Target.Property.PropertyType.Cast(targetValue)}); - else - b.Target.Property.GetSetMethod ().Invoke - (this, new object[] { targetValue }); - #endregion - - //if no dyn update, skip jump table - if (il == null) - continue; - - il.Emit (OpCodes.Ldloc_0); - if (b.Source.Member != null) - il.Emit (OpCodes.Ldstr, b.Source.Member.Name); - else - il.Emit (OpCodes.Ldstr, b.Expression.Split('/').LastOrDefault()); - il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal - il.Emit (OpCodes.Callvirt, stringEquals); - il.Emit (OpCodes.Brtrue, jumpTable[i]); - i++; - } - - if (il == null) - continue; - - il.Emit (OpCodes.Br, endMethod); - - i = 0; - foreach (Binding b in grouped) { - - il.MarkLabel (jumpTable [i]); - il.Emit(OpCodes.Ldloc_1); - - //by default, target value type is deducted from source member type to allow - //memberless binding, if targetMember exists, it will be used to determine target - //value type for conversion - Type targetValueType = b.Target.Property.PropertyType; - if (b.Source.Member != null) { - if (b.Source.Member.MemberType == MemberTypes.Property) - targetValueType = b.Source.Property.PropertyType; - else if (b.Source.Member.MemberType == MemberTypes.Field) - targetValueType = b.Source.Field.FieldType; - else - throw new Exception ("unhandle target member type in binding"); - } - - if (b.Target.Property.PropertyType == typeof(string)) { - MemberReference tostring = new MemberReference (b.Target.Instance); - if (!tostring.FindMember ("ToString")) - throw new Exception ("ToString method not found"); - il.Emit (OpCodes.Callvirt, tostring.Method); - }else if (!targetValueType.IsValueType) - il.Emit(OpCodes.Castclass, targetValueType); - else if (b.Target.Property.PropertyType != targetValueType) - il.Emit(OpCodes.Callvirt, CompilerServices.GetConvertMethod( b.Target.Property.PropertyType )); - else - il.Emit(OpCodes.Unbox_Any, b.Target.Property.PropertyType); - il.Emit(OpCodes.Callvirt, b.Target.Property.GetSetMethod()); - il.Emit (OpCodes.Br, endMethod); - i++; - - } - il.MarkLabel(endMethod); - il.Emit(OpCodes.Pop); - il.Emit(OpCodes.Ret); - - Delegate del = dm.CreateDelegate(ei.EventHandlerType, this); - MethodInfo addHandler = ei.GetAddMethod (); - addHandler.Invoke(grouped [0].Source.Instance, new object[] {del}); - } + CompilerServices.ResolveBindings (Bindings); } - /// <summary> - /// Compile events expression in GOML attributes - /// </summary> - /// <param name="es">Event binding details</param> - public void CompileEventSource(Binding binding) - { - #if DEBUG_BINDING - Debug.WriteLine ("\tCompile Event Source => " + binding.ToString()); - #endif - - Type sourceType = this.GetType(); - - #region Retrieve EventHandler parameter type - MethodInfo evtInvoke = binding.Target.Event.EventHandlerType.GetMethod ("Invoke"); - ParameterInfo[] evtParams = evtInvoke.GetParameters (); - Type handlerArgsType = evtParams [1].ParameterType; - #endregion - - Type[] args = {typeof(object), typeof(object),handlerArgsType}; - DynamicMethod dm = new DynamicMethod(binding.NewDynMethodId, - typeof(void), - args, - sourceType); - - #region IL generation - ILGenerator il = dm.GetILGenerator(256); - - string src = binding.Expression.Trim(); - - if (! (src.StartsWith("{") || src.EndsWith ("}"))) - throw new Exception (string.Format("GOML:Malformed {0} Event handler: {1}", binding.Target.Member.Name, binding.Expression)); - - src = src.Substring (1, src.Length - 2); - string[] srcLines = src.Split (new char[] { ';' }); - - foreach (string srcLine in srcLines) { - string statement = srcLine.Trim (); - - string[] operandes = statement.Split (new char[] { '=' }); - if (operandes.Length < 2) //not an affectation - { - continue; - } - string lop = operandes [0].Trim (); - string rop = operandes [operandes.Length-1].Trim (); - - #region LEFT OPERANDES - GraphicObject lopObj = this; //default left operand base object is - //the first arg (object sender) of the event handler - - il.Emit(OpCodes.Ldarg_0); //load sender ref onto the stack - - string[] lopParts = lop.Split (new char[] { '.' }); - if (lopParts.Length > 1) {//should search also for member of es.Source - MethodInfo FindByNameMi = typeof(GraphicObject).GetMethod("FindByName"); - for (int j = 0; j < lopParts.Length - 1; j++) { - il.Emit (OpCodes.Ldstr, lopParts[j]); - il.Emit(OpCodes.Callvirt, FindByNameMi); - } - } - - int i = lopParts.Length -1; - - MemberInfo[] lopMbis = lopObj.GetType().GetMember (lopParts[i]); - - if (lopMbis.Length<1) - throw new Exception (string.Format("CROW BINDING: Member not found '{0}'", lop)); - - OpCode lopSetOC; - dynamic lopSetMbi; - Type lopT = null; - switch (lopMbis[0].MemberType) { - case MemberTypes.Property: - PropertyInfo lopPi = sourceType.GetProperty (lopParts[i]); - MethodInfo dstMi = lopPi.GetSetMethod (); - lopT = lopPi.PropertyType; - lopSetMbi = dstMi; - lopSetOC = OpCodes.Callvirt; - break; - case MemberTypes.Field: - FieldInfo dstFi = sourceType.GetField(lopParts[i]); - lopT = dstFi.FieldType; - lopSetMbi = dstFi; - lopSetOC = OpCodes.Stfld; - break; - default: - throw new Exception (string.Format("GOML:member type not handle: {0}", lopParts[i])); - } - #endregion - - #region RIGHT OPERANDES - if (rop.StartsWith("\'")){ - if (!rop.EndsWith("\'")) - throw new Exception (string.Format - ("GOML:malformed string constant in handler: {0}", rop)); - string strcst = rop.Substring (1, rop.Length - 2); - - il.Emit(OpCodes.Ldstr,strcst); - - }else{ - if (lopT.IsEnum) - throw new NotImplementedException(); - - MethodInfo lopParseMi = lopT.GetMethod("Parse"); - if (lopParseMi == null) - throw new Exception (string.Format - ("GOML:no parse method found in: {0}", lopT.Name)); - il.Emit(OpCodes.Ldstr, rop); - il.Emit(OpCodes.Callvirt, lopParseMi); - il.Emit(OpCodes.Unbox_Any, lopT); - } - - #endregion - - //emit left operand assignment - il.Emit(lopSetOC, lopSetMbi); - } - - il.Emit(OpCodes.Ret); - - #endregion - - Delegate del = dm.CreateDelegate(binding.Target.Event.EventHandlerType,this); - MethodInfo addHandler = binding.Target.Event.GetAddMethod (); - addHandler.Invoke(this, new object[] {del}); - - binding.Resolved = true; - } /// <summary> /// Remove dynamic delegates by ids from dataSource /// and delete ref of this in Shared interface refs diff --git a/src/GraphicObjects/IBindable.cs b/src/GraphicObjects/IBindable.cs new file mode 100644 index 00000000..642c1e9e --- /dev/null +++ b/src/GraphicObjects/IBindable.cs @@ -0,0 +1,31 @@ +// +// IValueChange.cs +// +// Author: +// Jean-Philippe Bruyère <jp.bruyere@hotmail.com> +// +// Copyright (c) 2015 jp +// +// 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 <http://www.gnu.org/licenses/>. +using System; +using System.Collections.Generic; + +namespace Crow +{ + public interface IBindable + { + List<Binding> Bindings { get; } + } +} + diff --git a/src/GraphicObjects/TextBox.cs b/src/GraphicObjects/TextBox.cs index 14cc1830..08ec1f69 100644 --- a/src/GraphicObjects/TextBox.cs +++ b/src/GraphicObjects/TextBox.cs @@ -80,9 +80,9 @@ namespace Crow case Key.Back: if (!selectionIsEmpty) { -// Text = Text.Remove(selectionStart, selectionEnd - selectionStart); -// selReleasePos = -1; -// currentCol = selBeginPos; + // Text = Text.Remove(selectionStart, selectionEnd - selectionStart); + // selReleasePos = -1; + // currentCol = selBeginPos; } else this.DeleteChar(); @@ -142,61 +142,26 @@ namespace Crow case Key.RWin: break; case Key.Tab: - this.Insert("\t"); - break; - case Key.KeypadDecimal: - this.Insert ("."); - break; - case Key.Space: - this.Insert(" "); - break; - case Key.KeypadDivide: - case Key.Slash: - this.Insert("/"); - break; - case Key.KeypadMultiply: - this.Insert("*"); - break; - case Key.KeypadMinus: - case Key.Minus: - this.Insert("-"); - break; - case Key.KeypadPlus: - case Key.Plus: - this.Insert("+"); - break; - case Key.ShiftLeft: - case Key.ShiftRight: - case Key.AltLeft: - case Key.AltRight: - break; - case Key.Semicolon: - this.Insert(";"); + this.Insert ("\t"); break; default: - if (!selectionIsEmpty) - { -// Text = Text.Remove(selectionStart, selectionEnd - selectionStart); -// currentCol = selBeginPos; - } - - string k = "?"; - if ((char)key >= 67 && (char)key <= 76) - k = ((int)key - 67).ToString(); - else if ((char)key >= 109 && (char)key <= 118) - k = ((int)key - 109).ToString(); - else if (e.Shift) - k = key.ToString(); - else - k = key.ToString().ToLower(); + break; + } + if (Width < 0) + RegisterForLayouting (LayoutingType.Width); + if (Height < 0) + RegisterForLayouting (LayoutingType.Height); + RegisterForGraphicUpdate(); + } + public override void onKeyPress (object sender, KeyPressEventArgs e) + { + base.onKeyPress (sender, e); - this.Insert (k); + this.Insert (e.KeyChar.ToString()); - SelRelease = -1; - SelBegin = new Point(CurrentColumn, SelBegin.Y); + SelRelease = -1; + SelBegin = new Point(CurrentColumn, SelBegin.Y); - break; - } if (Width < 0) RegisterForLayouting (LayoutingType.Width); if (Height < 0) |