diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeDeserializer.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeDeserializer.cs index 4b7d43f88e..7709ad2401 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeDeserializer.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeDeserializer.cs @@ -124,7 +124,7 @@ namespace ICSharpCode.PythonBinding } } } - return componentCreator.GetComponent(PythonControlFieldExpression.GetVariableName(field.MemberName)); + return componentCreator.GetInstance(PythonControlFieldExpression.GetVariableName(field.MemberName)); } Type GetType(PythonControlFieldExpression field) diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonComponentWalker.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonComponentWalker.cs index 57f26654d4..df76014942 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonComponentWalker.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonComponentWalker.cs @@ -195,12 +195,25 @@ namespace ICSharpCode.PythonBinding return SetPropertyValue(GetCurrentComponent(), name, propertyValue); } + /// + /// Returns true if the current component has a property with the specified name. + /// + bool HasPropertyValue(string name) + { + return GetPropertyDescriptor(GetCurrentComponent(), name) != null; + } + + PropertyDescriptor GetPropertyDescriptor(object component, string name) + { + return TypeDescriptor.GetProperties(component).Find(name, true); + } + /// /// Sets the value of a property on the component. /// bool SetPropertyValue(object component, string name, object propertyValue) { - PropertyDescriptor property = TypeDescriptor.GetProperties(component).Find(name, true); + PropertyDescriptor property = GetPropertyDescriptor(component, name); if (property != null) { propertyValue = ConvertPropertyValue(property, propertyValue); property.SetValue(component, propertyValue); @@ -232,10 +245,13 @@ namespace ICSharpCode.PythonBinding /// /// Adds a component to the list of created objects. /// - void AddComponent(string name, object component) + void AddComponent(string name, object obj) { - string variableName = PythonControlFieldExpression.GetVariableName(name); - componentCreator.Add(component as IComponent, variableName); + IComponent component = obj as IComponent; + if (component != null) { + string variableName = PythonControlFieldExpression.GetVariableName(name); + componentCreator.Add(component, variableName); + } } /// @@ -289,7 +305,8 @@ namespace ICSharpCode.PythonBinding { MemberExpression memberExpression = node.Target as MemberExpression; if (memberExpression != null) { - object instance = CreateInstance(null, node); + string name = GetInstanceName(fieldExpression); + object instance = CreateInstance(name, node); if (instance != null) { if (!SetPropertyValue(fieldExpression.MemberName, instance)) { AddComponent(fieldExpression.MemberName, instance); @@ -305,6 +322,18 @@ namespace ICSharpCode.PythonBinding } } + /// + /// Gets the name of the instance. If the name matches a property of the current component being created + /// then this method returns null. + /// + string GetInstanceName(PythonControlFieldExpression fieldExpression) + { + if (!HasPropertyValue(fieldExpression.MemberName)) { + return PythonControlFieldExpression.GetVariableName(fieldExpression.MemberName); + } + return null; + } + /// /// Walks a method call. Typical method calls are: /// diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonControl.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonControl.cs index 53d8e4b954..abb11a3082 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonControl.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonControl.cs @@ -72,10 +72,15 @@ namespace ICSharpCode.PythonBinding void GenerateInitializeComponentMethodBodyInternal(Control control) { - PythonDesignerComponent rootDesignerComponent = PythonDesignerComponentFactory.CreateDesignerRootComponent(control); + PythonDesignerRootComponent rootDesignerComponent = PythonDesignerComponentFactory.CreateDesignerRootComponent(control); + if (rootDesignerComponent.HasNonVisualChildComponents()) { + rootDesignerComponent.AppendCreateComponentsContainer(codeBuilder); + rootDesignerComponent.AppendCreateNonVisualComponents(codeBuilder); + } rootDesignerComponent.AppendCreateChildComponents(codeBuilder); rootDesignerComponent.AppendChildComponentsSuspendLayout(codeBuilder); rootDesignerComponent.AppendSuspendLayout(codeBuilder); + rootDesignerComponent.AppendNonVisualComponents(codeBuilder); rootDesignerComponent.AppendComponent(codeBuilder); rootDesignerComponent.AppendChildComponentsResumeLayout(codeBuilder); rootDesignerComponent.AppendResumeLayout(codeBuilder); diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerComponent.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerComponent.cs index 507e4b1fb7..eea0f5fbc1 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerComponent.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerComponent.cs @@ -122,7 +122,34 @@ namespace ICSharpCode.PythonBinding } } return false; - } + } + + /// + /// Returns true if the component has the DesignTimeVisible attribute set to false. + /// + public static bool IsHiddenFromDesigner(IComponent component) + { + foreach (DesignTimeVisibleAttribute attribute in component.GetType().GetCustomAttributes(typeof(DesignTimeVisibleAttribute), true)) { + return !attribute.Visible; + } + return false; + } + + /// + /// A component is non-visual if it is not a control and is not hidden from the designer. + /// + public static bool IsNonVisualComponent(IComponent component) + { + Control control = component as Control; + return (control == null) && !IsHiddenFromDesigner(component); + } + + /// + /// Returns true if this component is non-visual. + /// + public bool IsNonVisual { + get { return IsNonVisualComponent(component); } + } /// /// Gets the AddRange method on the object that is not hidden from the designer. @@ -188,6 +215,11 @@ namespace ICSharpCode.PythonBinding AppendComponentCreation(codeBuilder, component); } + public void AppendCreateInstance(PythonCodeBuilder codeBuilder, string parameters) + { + AppendComponentCreation(codeBuilder, component, parameters); + } + /// /// Appends the code to create the child components. /// @@ -294,12 +326,11 @@ namespace ICSharpCode.PythonBinding designerComponent.AppendChildComponentsMethodCalls(codeBuilder, methods); } } - - + /// /// Appends the code to create the specified object. /// - public void AppendObjectCreation(PythonCodeBuilder codeBuilder, object obj, int count, object[] parameters) + public void AppendCreateInstance(PythonCodeBuilder codeBuilder, object obj, int count, object[] parameters) { if (obj is String) { // Do nothing. @@ -330,9 +361,17 @@ namespace ICSharpCode.PythonBinding /// public void AppendComponentCreation(PythonCodeBuilder codeBuilder, IComponent component) { - codeBuilder.AppendIndentedLine("self._" + component.Site.Name + " = " + component.GetType().FullName + "()"); + AppendComponentCreation(codeBuilder, component, String.Empty); } - + + /// + /// Appends the code to create the specified IComponent + /// + public void AppendComponentCreation(PythonCodeBuilder codeBuilder, IComponent component, string parameters) + { + codeBuilder.AppendIndentedLine("self._" + component.Site.Name + " = " + component.GetType().FullName + "(" + parameters + ")"); + } + /// /// Generates the code for the component's properties. /// diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerComponentFactory.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerComponentFactory.cs index 40d6cf5de5..deaf032599 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerComponentFactory.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerComponentFactory.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.PythonBinding return new PythonDesignerComponent(component); } - public static PythonDesignerComponent CreateDesignerRootComponent(IComponent component) + public static PythonDesignerRootComponent CreateDesignerRootComponent(IComponent component) { return new PythonDesignerRootComponent(component); } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerRootComponent.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerRootComponent.cs index 7ebfa7fce6..2bd43da85f 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerRootComponent.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonDesignerRootComponent.cs @@ -6,7 +6,9 @@ // using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Windows.Forms; namespace ICSharpCode.PythonBinding { @@ -54,6 +56,60 @@ namespace ICSharpCode.PythonBinding PythonDesignerComponent[] components = base.GetChildComponents(); Array.Reverse(components); return components; - } + } + + /// + /// Returns true if non-visual components (e.g. Timer) are associated with this root component. + /// + public bool HasNonVisualChildComponents() + { + foreach (IComponent containerComponent in Component.Site.Container.Components) { + if (IsNonVisualComponent(containerComponent)) { + return true; + } + } + return false; + } + + public PythonDesignerComponent[] GetNonVisualChildComponents() + { + List components = new List(); + foreach (IComponent containerComponent in Component.Site.Container.Components) { + PythonDesignerComponent designerComponent = PythonDesignerComponentFactory.CreateDesignerComponent(containerComponent); + if (designerComponent.IsNonVisual) { + components.Add(designerComponent); + } + } + return components.ToArray(); + } + + /// + /// Appends an expression that creates an instance of the Container to hold non-visual components + /// + public void AppendCreateComponentsContainer(PythonCodeBuilder codeBuilder) + { + codeBuilder.AppendIndentedLine("self._components = " + typeof(Container).FullName + "()"); + } + + /// + /// Appends code to create all the non-visual component. + /// + public void AppendCreateNonVisualComponents(PythonCodeBuilder codeBuilder) + { + foreach (PythonDesignerComponent component in GetNonVisualChildComponents()) { + component.AppendCreateInstance(codeBuilder, "self._components"); + } + } + + /// + /// Appends code to set all the non-visual component properties. + /// + /// + public void AppendNonVisualComponents(PythonCodeBuilder codeBuilder) + { + foreach (PythonDesignerComponent component in GetNonVisualChildComponents()) { + component.AppendComponent(codeBuilder); + } + } } } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonListViewComponent.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonListViewComponent.cs index ff0c0a595f..0809a6bf71 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonListViewComponent.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonListViewComponent.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.PythonBinding // Append list view item creation first. int count = 1; foreach (ListViewItem item in GetListViewItems(Component)) { - AppendObjectCreation(codeBuilder, item, count, GetConstructorParameters(item)); + AppendCreateInstance(codeBuilder, item, count, GetConstructorParameters(item)); ++count; } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/DeserializeComponentAssignmentTestFixture.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/DeserializeComponentAssignmentTestFixture.cs index b2bbb3963e..9544054ec2 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/DeserializeComponentAssignmentTestFixture.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/DeserializeComponentAssignmentTestFixture.cs @@ -27,10 +27,7 @@ namespace PythonBinding.Tests.Designer public override string GetPythonCode() { - button = new Button(); - button.Name = "button1"; - base.componentCreator.Add(button, "button1"); - + button = (Button)base.componentCreator.CreateInstance(typeof(Button), new object[0], "button1", false); return "self.AcceptButton = self._button1"; } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateListViewItemTestFixture.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateListViewItemTestFixture.cs index f77edb0187..5629f34399 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateListViewItemTestFixture.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateListViewItemTestFixture.cs @@ -249,5 +249,11 @@ namespace PythonBinding.Tests.Designer Assert.AreEqual(expectedCode, createListViewChildComponentsCode); } + + [Test] + public void ColumnHeaderIsHiddenFromDesigner() + { + Assert.IsTrue(PythonDesignerComponent.IsHiddenFromDesigner(columnHeader1)); + } } } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateMenuStripItemsTestFixture.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateMenuStripItemsTestFixture.cs index 7b00aaa6b3..dd8a433bf5 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateMenuStripItemsTestFixture.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateMenuStripItemsTestFixture.cs @@ -26,6 +26,7 @@ namespace PythonBinding.Tests.Designer Size openMenuItemSize; Size exitMenuItemSize; Size editMenuItemSize; + bool nonVisualComponents; [TestFixtureSetUp] public void SetUpFixture() @@ -75,6 +76,9 @@ namespace PythonBinding.Tests.Designer exitMenuItemSize = exitMenuItem.Size; editMenuItemSize = editMenuItem.Size; + PythonDesignerRootComponent rootComponent = new PythonDesignerRootComponent(form); + nonVisualComponents = rootComponent.HasNonVisualChildComponents(); + PythonControl pythonForm = new PythonControl(" "); generatedPythonCode = pythonForm.GenerateInitializeComponentMethod(form); } @@ -142,10 +146,16 @@ namespace PythonBinding.Tests.Designer Assert.AreEqual(expectedCode, generatedPythonCode, generatedPythonCode); } - + + [Test] + public void NoNonVisualComponents() + { + Assert.IsFalse(nonVisualComponents); + } + string SizeToString(Size size) { return PythonPropertyValueAssignment.ToString(size); - } + } } } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateTimerTestFixture.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateTimerTestFixture.cs new file mode 100644 index 0000000000..5203012f43 --- /dev/null +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GenerateTimerTestFixture.cs @@ -0,0 +1,79 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Windows.Forms; +using ICSharpCode.PythonBinding; +using NUnit.Framework; +using PythonBinding.Tests.Utils; + +namespace PythonBinding.Tests.Designer +{ + [TestFixture] + public class GenerateTimerTestFixture + { + string generatedPythonCode; + bool hasNonVisualChildComponents; + + [TestFixtureSetUp] + public void SetUpFixture() + { + using (DesignSurface designSurface = new DesignSurface(typeof(Form))) { + IDesignerHost host = (IDesignerHost)designSurface.GetService(typeof(IDesignerHost)); + Form form = (Form)host.RootComponent; + form.ClientSize = new Size(284, 264); + + PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(form); + PropertyDescriptor namePropertyDescriptor = descriptors.Find("Name", false); + namePropertyDescriptor.SetValue(form, "MainForm"); + + Timer timer = (Timer)host.CreateComponent(typeof(Timer), "timer1"); + descriptors = TypeDescriptor.GetProperties(timer); + namePropertyDescriptor = descriptors.Find("Interval", false); + namePropertyDescriptor.SetValue(timer, 1000); + + string indentString = " "; + PythonDesignerRootComponent designerRootComponent = new PythonDesignerRootComponent(form); + hasNonVisualChildComponents = designerRootComponent.HasNonVisualChildComponents(); + + PythonControl pythonControl = new PythonControl(indentString); + generatedPythonCode = pythonControl.GenerateInitializeComponentMethod(form); + } + } + + [Test] + public void HasNonVisualChildComponents() + { + Assert.IsTrue(hasNonVisualChildComponents); + } + + [Test] + public void GeneratedCode() + { + string expectedCode = "def InitializeComponent(self):\r\n" + + " self._components = System.ComponentModel.Container()\r\n" + + " self._timer1 = System.Windows.Forms.Timer(self._components)\r\n" + + " self.SuspendLayout()\r\n" + + " # \r\n" + + " # timer1\r\n" + + " # \r\n" + + " self._timer1.Interval = 1000\r\n" + + " # \r\n" + + " # MainForm\r\n" + + " # \r\n" + + " self.ClientSize = System.Drawing.Size(284, 264)\r\n" + + " self.Name = \"MainForm\"\r\n" + + " self.ResumeLayout(False)\r\n" + + " self.PerformLayout()\r\n"; + + Assert.AreEqual(expectedCode, generatedPythonCode, generatedPythonCode); + } + } +} diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/LoadTextBoxTestFixture.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/LoadTextBoxTestFixture.cs index 2f2a99a5eb..656040c8b1 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/LoadTextBoxTestFixture.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/LoadTextBoxTestFixture.cs @@ -58,7 +58,7 @@ namespace PythonBinding.Tests.Designer [Test] public void TextBoxInstanceCreated() { - CreatedInstance instance = new CreatedInstance(typeof(TextBox), new List(), null, false); + CreatedInstance instance = new CreatedInstance(typeof(TextBox), new List(), "textBox1", false); Assert.Contains(instance, ComponentCreator.CreatedInstances); } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/LoadTimerTestFixture.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/LoadTimerTestFixture.cs new file mode 100644 index 0000000000..54edcf90f1 --- /dev/null +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/LoadTimerTestFixture.cs @@ -0,0 +1,66 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Windows.Forms; + +using ICSharpCode.PythonBinding; +using NUnit.Framework; +using PythonBinding.Tests.Utils; + +namespace PythonBinding.Tests.Designer +{ + [TestFixture] + public class LoadTimerTestFixture : LoadFormTestFixtureBase + { + public override string PythonCode { + get { + return "class MainForm(System.Windows.Forms.Form):\r\n" + + " def InitializeComponent(self):\r\n" + + " self._components = System.ComponentModel.Container()\r\n" + + " self._timer1 = System.Windows.Forms.Timer(self._components)\r\n" + + " self.SuspendLayout()\r\n" + + " # \r\n" + + " # timer1\r\n" + + " # \r\n" + + " self._timer1.Interval = 1000\r\n" + + " # \r\n" + + " # MainForm\r\n" + + " # \r\n" + + " self.ClientSize = System.Drawing.Size(300, 400)\r\n" + + " self.Name = \"MainForm\"\r\n" + + " self.ResumeLayout(False)\r\n"; + } + } + + public CreatedInstance TimerInstance { + get { return ComponentCreator.CreatedInstances[1]; } + } + + [Test] + public void ThreeInstancesCreated() + { + Assert.AreEqual(3, ComponentCreator.CreatedInstances.Count); + } + + + [Test] + public void ComponentName() + { + Assert.AreEqual("timer1", TimerInstance.Name); + } + + [Test] + public void ComponentType() + { + Assert.AreEqual("System.Windows.Forms.Timer", TimerInstance.InstanceType.FullName); + } + } +} diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj b/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj index c628ddcc54..ee3ee69f22 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj @@ -182,6 +182,7 @@ + @@ -208,6 +209,7 @@ + diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/TODO.txt b/src/AddIns/BackendBindings/Python/PythonBinding/Test/TODO.txt index 285b843e25..d07ca80315 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/TODO.txt +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/TODO.txt @@ -94,4 +94,8 @@ def main(args): See http://lists.ironpython.com/pipermail/users-ironpython.com/2009-April/009993.html - * Support for ListViewSubItems - Font, Colors. \ No newline at end of file + * Support for ListViewSubItems - Font, Colors. + + * Timer - Honour the GenerateMember setting. + + * Look at creating code for non-visual components in the same order as Visual Studio. \ No newline at end of file diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockComponentCreator.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockComponentCreator.cs index 5b200d69a7..2bfe6f4dfd 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockComponentCreator.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockComponentCreator.cs @@ -42,6 +42,10 @@ namespace PythonBinding.Tests.Utils public void Add(IComponent component, string name) { + if (component == null) { + throw new ArgumentNullException("component"); + } + AddedComponent addedComponent = new AddedComponent(component, name); addedComponents.Add(addedComponent); } @@ -81,12 +85,15 @@ namespace PythonBinding.Tests.Utils // Lookup type in System.Windows.Forms assembly. Type type = typeof(Form).Assembly.GetType(typeName); if (type == null) { - // Lookup type System.Drawing assembly. + // Lookup type in System.Drawing assembly. type = typeof(Size).Assembly.GetType(typeName); } if (type == null) { type = typeof(String).Assembly.GetType(typeName); } + if (type == null) { + type = typeof(Component).Assembly.GetType(typeName); + } return type; }