2
0
Эх сурвалжийг харах

Added basic Binding support

Dexter89 12 жил өмнө
parent
commit
ab713f90fb

+ 71 - 0
tools/gui/crown-tests/GtkExt/BindingEngine.cs

@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+
+namespace crown_tests
+{
+	public static class BindingEngine
+	{
+		private static Dictionary<IBindingTarget, Binding> mBindings;
+
+		static BindingEngine() {
+			mBindings = new Dictionary<IBindingTarget, Binding>();
+		}
+
+		public static Binding GetOrCreateBinding(Object source, IBindingTarget target, BindingInfo info) {
+			Binding result = null;
+			if (!mBindings.TryGetValue(target, out result)) {
+				result = new Binding();
+				result.Info = info;
+				result.Connect(source, target);
+				mBindings[target] = result;
+			}
+			return result;
+		}
+	}
+
+	//BindingInfo holds binding informations shared among all Bindings of the same type. For example, a TreeViewRowTemplate that is applied to 
+	//multiple rows has a BindingInfo for each column, that is shared among Binding instance for single rows.
+	public class BindingInfo
+	{
+		public String Path;
+		public Func<object, object> Converter;
+	}
+
+	//A Binding si an association between two properties. Usually the source is a ViewModel property and the Target a View property. 
+	//The Source and the Target are determined based on the context of application: When inserted in a Template, upon first application the two
+	//are determined and set, attaching necessary event handlers to manage the update when one of them changes.
+	public class Binding
+	{
+		public BindingInfo Info;
+
+		private Object mSource;
+		private IBindingTarget mTarget;
+
+		public void Connect(Object source, IBindingTarget target) {
+			mSource = source;
+			mTarget = target;
+
+			var propertyChanged = mSource as IPropertyChanged;
+			if (propertyChanged != null) {
+				propertyChanged.PropertyChanged += (object sender, PropertyChangedEventArgs e) => mTarget.Update(this, GetSourceValue());
+			}
+		}
+
+		public Object GetSourceValue() {
+			var propInfo = mSource.GetType().GetProperty(Info.Path);
+			if (propInfo == null)
+				return null;
+
+			var propValue = propInfo.GetValue(mSource, null);
+			if (Info.Converter != null)
+				propValue = Info.Converter(propValue);
+			return propValue;
+		}
+	}
+
+	public interface IBindingTarget
+	{
+		void Update(Binding binding, Object newValue);
+	}
+}
+

+ 20 - 0
tools/gui/crown-tests/GtkExt/IPropertyChanged.cs

@@ -0,0 +1,20 @@
+using System;
+
+namespace crown_tests
+{
+	public class PropertyChangedEventArgs: EventArgs
+	{
+		public readonly String PropertyName;
+
+		public PropertyChangedEventArgs(String propName)
+		{
+			PropertyName = propName;
+		}
+	}
+	public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
+	public interface IPropertyChanged
+	{
+		event PropertyChangedEventHandler PropertyChanged;
+	}
+}
+

+ 4 - 10
tools/gui/crown-tests/GtkExt/TreeViewRowTemplate.cs

@@ -6,12 +6,12 @@ namespace crown_tests.GtkExt
 	public class TreeViewRowTemplate
 	{
 		public Type TargetType;
-		public Dictionary<String, Binding> ColumnBindings;
+		public Dictionary<String, BindingInfo> ColumnBindings;
 
 		public TreeViewRowTemplate(Type targetType)
 		{
 			this.TargetType = targetType;
-			this.ColumnBindings = new Dictionary<string, Binding>();
+			this.ColumnBindings = new Dictionary<string, BindingInfo>();
 		}
 
 		public static TreeViewRowTemplate Create(Type targetType)
@@ -21,20 +21,14 @@ namespace crown_tests.GtkExt
 
 		public TreeViewRowTemplate SetBinding(String colName, String path)
 		{
-			ColumnBindings.Add(colName, new Binding() { Path = path });
+			ColumnBindings.Add(colName, new BindingInfo() { Path = path });
 			return this;
 		}
 
 		public TreeViewRowTemplate SetBinding(String colName, String path, Func<object, object> Converter)
 		{
-			ColumnBindings.Add(colName, new Binding() { Path = path, Converter = Converter });
+			ColumnBindings.Add(colName, new BindingInfo() { Path = path, Converter = Converter });
 			return this;
 		}
 	}
-
-	public class Binding
-	{
-		public String Path;
-		public Func<object, object> Converter;
-	}
 }

+ 45 - 9
tools/gui/crown-tests/GtkExt/TreeViewTemplating.cs

@@ -36,7 +36,8 @@ namespace crown_tests.GtkExt
 
 		private static void ValuePropertyDataFunc(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
 		{
-			var info = GetInfo((Gtk.TreeView)column.TreeView);
+			var treeView = (Gtk.TreeView)column.TreeView;
+			var info = GetInfo(treeView);
 
 			var textCell = (cell as Gtk.CellRendererText);
 			textCell.Text = string.Empty;
@@ -46,17 +47,16 @@ namespace crown_tests.GtkExt
 
 			foreach (var rowTemplate in info.RowTemplates) {
 				if (value.GetType() == rowTemplate.TargetType) {
-					Binding binding = null;
-					if (!rowTemplate.ColumnBindings.TryGetValue(column.Title, out binding))
-						return;
+					//Here we have a value, which is the source for Binding, and a BindingInfo that is given by rowTemplate.ColumnBindings[column.Title] .
+					//The instance of the BindingInfo is shared among all values (rows), since it was defined once in the rowTemplate.
 
-					var propInfo = value.GetType().GetProperty(binding.Path);
-					if (propInfo == null)
+					BindingInfo bindingInfo = null;
+					if (!rowTemplate.ColumnBindings.TryGetValue(column.Title, out bindingInfo))
 						return;
 
-					var propValue = propInfo.GetValue(value, null);
-					if (binding.Converter != null)
-						propValue = binding.Converter(propValue);
+					//The actual binding, on the other hand, is specific to the current (row,column) pair.
+					Binding binding = BindingEngine.GetOrCreateBinding(value, new TreeViewIterBindingTarget(treeView, iter, column), bindingInfo);
+					var propValue = binding.GetSourceValue();
 					textCell.Text = propValue == null ? String.Empty : propValue.ToString();
 					return;
 				}
@@ -67,6 +67,42 @@ namespace crown_tests.GtkExt
 		{
 			public List<TreeViewRowTemplate> RowTemplates = new List<TreeViewRowTemplate>();
 		}
+
+		private class TreeViewIterBindingTarget: IBindingTarget
+		{
+			private Gtk.TreeIter mIter;
+			private Gtk.TreeView mTreeView;
+			private Gtk.TreeViewColumn mColumn;
+
+			public TreeViewIterBindingTarget(Gtk.TreeView treeView, Gtk.TreeIter iter, Gtk.TreeViewColumn column)
+			{
+				mTreeView = treeView;
+				mIter = iter;
+				mColumn = column;
+			}
+
+			public override bool Equals(object obj)
+			{
+				var other = obj as TreeViewIterBindingTarget;
+				if (other == null)
+					return false;
+				return other.mIter.Equals(mIter) && (mTreeView == other.mTreeView) && (mColumn == other.mColumn);
+			}
+
+			public override int GetHashCode()
+			{
+				return mIter.GetHashCode();
+			}
+
+			#region IBindingTarget implementation
+
+			public void Update(Binding binding, object newValue)
+			{
+				mTreeView.Model.EmitRowChanged(mTreeView.Model.GetPath(mIter), mIter);
+			}
+
+			#endregion
+		}
 	}
 }
 

+ 24 - 0
tools/gui/crown-tests/GtkExt/ViewModelBase.cs

@@ -0,0 +1,24 @@
+using System;
+
+namespace crown_tests
+{
+	public class ViewModelBase: IPropertyChanged
+	{
+		public ViewModelBase()
+		{
+		}
+
+		#region IPropertyChanged implementation
+
+		public event PropertyChangedEventHandler PropertyChanged;
+
+		protected void Notify(String propertyName)
+		{
+			if (PropertyChanged != null)
+				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+		}
+
+		#endregion
+	}
+}
+

+ 0 - 1
tools/gui/crown-tests/MainWindow.cs

@@ -49,7 +49,6 @@ namespace crown_tests
 		{
 			var executor = new TestExecutor(mContainer, txtCrownTestsExe.Text);
 			executor.ExecuteAll();
-			RefreshData();
 		}
 
 		private void LoadConfigData()

+ 3 - 0
tools/gui/crown-tests/crown-tests.csproj

@@ -72,6 +72,9 @@
     <Compile Include="MainWindow.cs" />
     <Compile Include="GtkExt\TreeViewTemplating.cs" />
     <Compile Include="GtkExt\TreeViewRowTemplate.cs" />
+    <Compile Include="GtkExt\IPropertyChanged.cs" />
+    <Compile Include="GtkExt\ViewModelBase.cs" />
+    <Compile Include="GtkExt\BindingEngine.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 2 - 2
tools/gui/crown-tests/gtk-gui/crown_tests.MainWindow.cs

@@ -28,6 +28,8 @@ namespace crown_tests
 			this.Name = "crown_tests.MainWindow";
 			this.Title = global::Mono.Unix.Catalog.GetString ("Test Browser");
 			this.WindowPosition = ((global::Gtk.WindowPosition)(4));
+			this.DefaultWidth = 500;
+			this.DefaultHeight = 350;
 			// Container child crown_tests.MainWindow.Gtk.Container+ContainerChild
 			this.vbox1 = new global::Gtk.VBox ();
 			this.vbox1.Name = "vbox1";
@@ -159,8 +161,6 @@ namespace crown_tests
 			if ((this.Child != null)) {
 				this.Child.ShowAll ();
 			}
-			this.DefaultWidth = 400;
-			this.DefaultHeight = 300;
 			this.Show ();
 			this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent);
 			this.btnCreate.Clicked += new global::System.EventHandler (this.btnCreate_Click);

+ 2 - 0
tools/gui/crown-tests/gtk-gui/gui.stetic

@@ -11,6 +11,8 @@
     <property name="MemberName" />
     <property name="Title" translatable="yes">Test Browser</property>
     <property name="WindowPosition">CenterOnParent</property>
+    <property name="DefaultWidth">500</property>
+    <property name="DefaultHeight">350</property>
     <signal name="DeleteEvent" handler="OnDeleteEvent" />
     <child>
       <widget class="Gtk.VBox" id="vbox1">

+ 22 - 11
tools/gui/crown-tests/tests/Test.cs

@@ -6,19 +6,30 @@ using System.Text;
 
 namespace crown_tests.tests
 {
-  [JsonObject(MemberSerialization.OptIn)]
-  public class Test
-  {
-    [JsonProperty]
+	[JsonObject(MemberSerialization.OptIn)]
+	public class Test: ViewModelBase
+	{
+		[JsonProperty]
 		public String Name { get; set; }
-    [JsonProperty]
+
+		[JsonProperty]
 		public String Description { get; set; }
 
-		public int LastResult { get; set; }
+		int mLastResult;
+
+		public int LastResult { 
+			get { return mLastResult; }
+			set {
+				if (mLastResult != value) {
+					mLastResult = value;
+					Notify("LastResult");
+				}
+			}
+		}
 
-    public String GetFunctionName()
-    {
-      return "test_" + Name.ToLower().Replace(' ', '_');
-    }
-  }
+		public String GetFunctionName()
+		{
+			return "test_" + Name.ToLower().Replace(' ', '_');
+		}
+	}
 }