Exemple #1
0
        /// <summary>
        /// Get properties values from our generated input fields
        /// </summary>
        private void FetchPropertiesValue(T aObject)
        {
            int ctrlIndex = 0;

            //For each of our properties
            foreach (PropertyInfo pi in aObject.GetType().GetProperties())
            {
                //Get our property attribute
                AttributeObjectProperty[] attributes = ((AttributeObjectProperty[])pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
                if (attributes.Length != 1)
                {
                    //No attribute, skip this property then.
                    continue;
                }
                AttributeObjectProperty attribute = attributes[0];

                //Check that we support this type of property
                if (!IsPropertyTypeSupported(pi))
                {
                    continue;
                }

                //Now fetch our property value
                SetObjectPropertyValueFromControl(aObject, pi, iTableLayoutPanel.Controls[ctrlIndex + 1]); //+1 otherwise we get the label

                ctrlIndex += 2;                                                                            //Jump over the label too
            }
        }
Exemple #2
0
        /// <summary>
        /// Update our table layout.
        /// Will instantiated every field control as defined by our object.
        /// </summary>
        /// <param name="aLayout"></param>
        private void UpdateControls()
        {
            iTableLayoutPanel.SuspendLayout(); // Avoid flicker

            toolTip.RemoveAll();
            //Debug.Print("UpdateTableLayoutPanel")
            //First clean our current panel
            iTableLayoutPanel.Controls.Clear();
            iTableLayoutPanel.RowStyles.Clear();
            iTableLayoutPanel.ColumnStyles.Clear();
            iTableLayoutPanel.RowCount = 0;

            //We always want two columns: one for label and one for the field
            iTableLayoutPanel.ColumnCount = 2;
            iTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
            iTableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));


            if (Object == null)
            {
                iTableLayoutPanel.ResumeLayout(true);
                //Just drop it
                return;
            }

            // Tell our object to prepare for edit
            Object.CurrentState     = SharpLib.Ear.Object.State.PrepareEdit;
            Object.PropertyChanged -= PropertyChangedEventHandlerThreadSafe; // Most important to avoid accumulating handlers
            //
            UpdateStaticControls();

            //IEnumerable<PropertyInfo> properties = aObject.GetType().GetProperties().Where(
            //    prop => Attribute.IsDefined(prop, typeof(AttributeObjectProperty)));

            //TODO: Do this whenever a field changes
            iLabelBrief.Text = Object.Brief();


            foreach (PropertyInfo pi in Object.GetType().GetProperties())
            {
                AttributeObjectProperty[] attributes = ((AttributeObjectProperty[])pi.GetCustomAttributes(typeof(AttributeObjectProperty), true));
                if (attributes.Length != 1)
                {
                    continue;
                }

                AttributeObjectProperty attribute = attributes[0];

                //Before anything we need to check if that kind of property is supported by our UI
                //Create the editor
                Control ctrl = CreateControlForProperty(pi, attribute, Object);
                if (ctrl == null)
                {
                    //Property type not supported
                    continue;
                }

                // Associate our object property with that control then
                // That will enable us to update just that control when the property changes
                ctrl.Tag = pi;

                //Add a new row
                iTableLayoutPanel.RowCount++;
                iTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
                //Create the label
                Label label = new Label();
                label.AutoSize  = true;
                label.Dock      = DockStyle.Fill;
                label.TextAlign = ContentAlignment.MiddleCenter;
                label.Text      = attribute.Name;
                toolTip.SetToolTip(label, attribute.Description);
                iTableLayoutPanel.Controls.Add(label, 0, iTableLayoutPanel.RowCount - 1);

                //Add our editor to our form
                iTableLayoutPanel.Controls.Add(ctrl, 1, iTableLayoutPanel.RowCount - 1);
                //Add tooltip to editor too
                toolTip.SetToolTip(ctrl, attribute.Description);
            }

            //Enter object edit mode
            Object.CurrentState     = SharpLib.Ear.Object.State.Edit;
            Object.PropertyChanged += PropertyChangedEventHandlerThreadSafe;

            iTableLayoutPanel.ResumeLayout(true);
        }
Exemple #3
0
        /// <summary>
        /// Create a control for the given property.
        /// TODO: Handle cases where a property value is null. That can be the case when extending an existing class and loading it from an older save.
        /// Though I guess that should really be taken care of in Ear.Object.Construct which is called once the object was internalized.
        /// </summary>
        /// <param name="aInfo"></param>
        /// <param name="aAttribute"></param>
        /// <param name="aObject"></param>
        /// <returns></returns>
        private Control CreateControlForProperty(PropertyInfo aInfo, AttributeObjectProperty aAttribute, T aObject)
        {
            if (aInfo.PropertyType == typeof(int) || aInfo.PropertyType == typeof(float))
            {
                //Integer properties are using numeric editor
                NumericUpDown ctrl = new NumericUpDown();
                ctrl.AutoSize = true;
                //ctrl.Dock = DockStyle.Fill; // Fill the whole table cell
                ctrl.Minimum       = (decimal)aAttribute.Minimum;
                ctrl.Maximum       = (decimal)aAttribute.Maximum;
                ctrl.Increment     = (decimal)aAttribute.Increment;
                ctrl.DecimalPlaces = aAttribute.DecimalPlaces;
                ctrl.Value         = decimal.Parse(aInfo.GetValue(aObject).ToString());
                // Hook-in change notification after setting the value
                ctrl.ValueChanged += ControlValueChanged;
                return(ctrl);
            }
            else if (aInfo.PropertyType.IsEnum)
            {
                //Enum properties are using combo box
                ComboBox ctrl = new ComboBox();
                ctrl.AutoSize      = true;
                ctrl.Sorted        = true;
                ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
                //Data source is fine but it gives us duplicate entries for duplicated enum values
                //ctrl.DataSource = Enum.GetValues(aInfo.PropertyType);

                //Therefore we need to explicitly create our items
                Size cbSize = new Size(0, 0);
                foreach (string name in aInfo.PropertyType.GetEnumNames())
                {
                    ctrl.Items.Add(name.ToString());
                    Graphics g = this.CreateGraphics();
                    //Since combobox autosize would not work we need to measure text ourselves
                    SizeF size = g.MeasureString(name.ToString(), ctrl.Font);
                    cbSize.Width  = Math.Max(cbSize.Width, (int)size.Width);
                    cbSize.Height = Math.Max(cbSize.Height, (int)size.Height);
                }

                cbSize.Width += 10; // Account for margin

                //Make sure our combobox is large enough
                ctrl.MinimumSize = cbSize;

                // Instantiate our enum
                object enumValue = Activator.CreateInstance(aInfo.PropertyType);
                enumValue = aInfo.GetValue(aObject);
                //Set the current item
                ctrl.SelectedItem = enumValue.ToString();
                // Hook-in change notification after setting the value
                ctrl.SelectedIndexChanged += ControlValueChanged;

                return(ctrl);
            }
            else if (aInfo.PropertyType == typeof(bool))
            {
                CheckBox ctrl = new CheckBox();
                ctrl.AutoSize = true;
                ctrl.Text     = aAttribute.Description;
                ctrl.Checked  = (bool)aInfo.GetValue(aObject);
                // Hook-in change notification after setting the value
                ctrl.CheckedChanged += ControlValueChanged;
                return(ctrl);
            }
            else if (aInfo.PropertyType == typeof(string))
            {
                TextBox ctrl = new TextBox();
                ctrl.AutoSize = true;
                ctrl.Dock     = DockStyle.Fill; // Fill the whole table cell
                ctrl.Text     = (string)aInfo.GetValue(aObject);

                // Multiline setup
                ctrl.Multiline = aAttribute.Multiline;
                if (ctrl.Multiline)
                {
                    ctrl.AcceptsReturn = true;
                    ctrl.ScrollBars    = ScrollBars.Vertical;
                    // Adjust our height as needed
                    Size size = ctrl.Size;
                    size.Height     += ctrl.Font.Height * (aAttribute.HeightInLines - 1);
                    ctrl.MinimumSize = size;
                }

                // Hook-in change notification after setting the value
                ctrl.TextChanged += ControlValueChanged;
                return(ctrl);
            }
            else if (aInfo.PropertyType == typeof(PropertyFile))
            {
                // We have a file property
                // Create a button that will trigger the open file dialog to select our file.
                Button ctrl = new Button();
                ctrl.AutoSize = true;
                ctrl.Text     = ((PropertyFile)aInfo.GetValue(aObject)).FullPath;
                // Add lambda expression to Click event
                ctrl.Click += (sender, e) =>
                {
                    // Create open file dialog
                    OpenFileDialog ofd = new OpenFileDialog();
                    ofd.RestoreDirectory = true;
                    // Use file filter specified by our property
                    ofd.Filter = aAttribute.Filter;
                    // Show our dialog
                    if (SharpLib.Forms.DlgBox.ShowDialog(ofd) == DialogResult.OK)
                    {
                        // Fetch selected file name
                        ctrl.Text = ofd.FileName;
                    }
                };

                // Hook-in change notification after setting the value
                ctrl.TextChanged += ControlValueChanged;
                return(ctrl);
            }
            else if (aInfo.PropertyType == typeof(PropertyComboBox))
            {
                //ComboBox property
                PropertyComboBox property = ((PropertyComboBox)aInfo.GetValue(aObject));

                ComboBox ctrl = new ComboBox();
                ctrl.AutoSize = true;
                //ctrl.Sorted = property.Sorted; // Sorted is not supported and does not work using DataSource, can result in the wrong item being selected
                ctrl.DropDownStyle = ComboBoxStyle.DropDownList;
                // Use DataSource with optional DisplayMember and ValueMember, that also works for plain string collection
                ctrl.DisplayMember = property.DisplayMember;
                ctrl.ValueMember   = property.ValueMember;
                //ctrl.DataSource = ((PropertyComboBox)aInfo.GetValue(aObject)).Items;

                UpdateControlFromProperty(ctrl, aInfo, aObject);

                return(ctrl);
            }
            else if (aInfo.PropertyType == typeof(PropertyButton))
            {
                // We have a button property
                // Create a button that will trigger the custom action.
                Button ctrl = new Button();
                ctrl.AutoSize = true;
                ctrl.Text     = ((PropertyButton)aInfo.GetValue(aObject)).Text;
                // Hook in click event
                ctrl.Click += ((PropertyButton)aInfo.GetValue(aObject)).ClickEventHandler;
                // Hook-in change notification after setting the value
                ctrl.TextChanged += ControlValueChanged;
                return(ctrl);
            }
            else if (aInfo.PropertyType == typeof(PropertyCheckedListBox))
            {
                //CheckedListBox property
                PropertyCheckedListBox property = ((PropertyCheckedListBox)aInfo.GetValue(aObject));

                CheckedListBox ctrl = new CheckedListBox();
                ctrl.AutoSize     = true;
                ctrl.Sorted       = property.Sorted;
                ctrl.CheckOnClick = true;

                // Populate our box with list items
                foreach (string item in property.Items)
                {
                    int index = ctrl.Items.Add(item);
                    // Check items if needed
                    if (property.CheckedItems.Contains(item))
                    {
                        ctrl.SetItemChecked(index, true);
                    }
                }
                //
                // Hook-in change notification after setting the value
                // That's essentially making sure title/brief gets updated as items are checked or unchecked
                // This looks convoluted as we are working around the fact that ItemCheck event is triggered before the model is updated.
                // See: https://stackoverflow.com/a/48645552/3969362
                ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => ControlValueChanged(s, e)));
                return(ctrl);
            }

            //TODO: add support for other control type here
            return(null);
        }