/// <summary> /// User has changed the value of a cell. /// </summary> /// <param name="sender">Sender object</param> /// <param name="e">Event parameters</param> private void OnCellValueChanged(object sender, GridCellsChangedArgs e) { this.explorerPresenter.CommandHistory.ModelChanged -= this.OnModelChanged; foreach (GridCellChangedArgs cell in e.ChangedCells) { try { //need to subtract one for column index of the cell due to description column Model childmodel = this.childrenWithSameType[cell.ColIndex - 1] as Model; VariableProperty property = properties[cell.ColIndex - 1][cell.RowIndex]; object newValue = GetNewCellValue(property, cell.NewValue); this.SetPropertyValue(childmodel, property, newValue); if (newValue.GetType().IsEnum) { newValue = VariableProperty.GetEnumDescription(newValue as Enum); } grid.DataSource.Rows[cell.RowIndex][cell.ColIndex] = newValue; } catch (Exception ex) { explorerPresenter.MainPresenter.ShowError(ex); } } this.explorerPresenter.CommandHistory.ModelChanged += this.OnModelChanged; }
/// <summary> /// Called when the view is changed. Updates the model's state. /// </summary> /// <param name="sender">Sending object.</param> /// <param name="args">Event data.</param> protected void OnViewChanged(object sender, PropertyChangedEventArgs args) { // We don't want to refresh the entire view after applying the change // to the model, so we need to temporarily detach the ModelChanged handler. //presenter.CommandHistory.ModelChanged -= OnModelChanged; // Figure out which property of which object is being changed. PropertyInfo property = propertyMap[args.ID].Property; object changedObject = propertyMap[args.ID].Model; object newValue = args.NewValue; // When using a multi-line text editor for an IEnumerable property, the // new value returned from the view will contain the enumerable with one // element per line. The 'canonical' form of an enumerable as recognised by // the StringToObject function is csv. Therefore we need to convert from // lf-separated elements to comma-separated elements. This should probably // occur somewhere else. DisplayAttribute attrib; if (newValue is string str && property.PropertyType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && (attrib = property.GetCustomAttribute <DisplayAttribute>()) != null && attrib.Type == DisplayType.MultiLineText) { newValue = string.Join(",", str.Split(Environment.NewLine.ToCharArray())); } if (property.PropertyType.IsEnum && newValue is string enumDescription) { foreach (Enum value in Enum.GetValues(property.PropertyType)) { if (VariableProperty.GetEnumDescription(value) == enumDescription) { newValue = Enum.GetName(property.PropertyType, value); break; } } } // In some cases, the new value passed back from the view may be // already of the correct type. For example a boolean property // is editable via a checkbutton, so the view will return a bool. // However, most numbers are just rendered using an entry widget, // so the value from the view will be a string (e.g. 1e-6). if ((newValue == null || newValue is string) && property.PropertyType != typeof(string)) { if (newValue is string modelName && typeof(IModel).IsAssignableFrom(property.PropertyType)) { newValue = model.FindAllInScope(modelName).FirstOrDefault(m => property.PropertyType.IsAssignableFrom(m.GetType())); } else { newValue = ReflectionUtilities.StringToObject(property.PropertyType, (string)newValue, CultureInfo.CurrentCulture); } }
/// <summary> /// User has changed the value of a cell. Validate the change /// apply the change. /// </summary> /// <param name="sender">Sender object</param> /// <param name="args">Event parameters</param> private void OnCellsChanged(object sender, GridCellsChangedArgs args) { List <ChangeProperty.Property> changes = new List <ChangeProperty.Property>(); foreach (GridCellChangedArgs cell in args.ChangedCells) { if (cell.NewValue == cell.OldValue) { continue; // silently fail } // If there are multiple changed cells, each change will be // individually undoable. IVariable property = GetProperty(cell.RowIndex, cell.ColIndex); if (property == null) { continue; } // Parse the input string to the appropriate type. object newValue = GetNewPropertyValue(property, cell); // Update the value of the model's property. SetPropertyValue(property, newValue); // Update the value shown in the grid. object val = GetCellValue(property, cell.RowIndex, cell.ColIndex); // Special handling for enumerations, as we want to display the description, not the value if (val.GetType().IsEnum) { val = VariableProperty.GetEnumDescription(val as Enum); } grid.DataSource.Rows[cell.RowIndex][cell.ColIndex] = val; } UpdateReadOnlyProperties(); }
/// <summary> /// Format the grid. /// </summary> private void FormatGrid() { for (int i = 0; i < properties.Count; i++) { IGridCell cell = grid.GetCell(1, i); if (properties[i] is VariableObject) { cell.EditorType = EditorTypeEnum.TextBox; grid.SetRowAsSeparator(i, true); } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.TableName) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = storage.Reader.TableNames.ToArray(); } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.CultivarName) { cell.EditorType = EditorTypeEnum.DropDown; IPlant crop = GetCrop(properties); if (crop != null) { cell.DropDownStrings = GetCultivarNames(crop); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.FileName) { cell.EditorType = EditorTypeEnum.Button; } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.FieldName) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = GetFieldNames(); if (fieldNames != null) { fieldNames.Insert(0, string.Empty); cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.ResidueName && model is SurfaceOrganicMatter) { cell.EditorType = EditorTypeEnum.DropDown; string[] fieldNames = GetResidueNames(); if (fieldNames != null) { cell.DropDownStrings = fieldNames; } } else if (properties[i].Display != null && (properties[i].Display.Type == DisplayType.CLEMResourceName)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = new List <string>(); fieldNames.AddRange(this.GetCLEMResourceNames(this.properties[i].Display.CLEMResourceNameResourceGroups)); // add any extras elements provided to the list. if (this.properties[i].Display.CLEMExtraEntries != null) { fieldNames.AddRange(this.properties[i].Display.CLEMExtraEntries); } if (fieldNames.Count != 0) { cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && (properties[i].Display.Type == DisplayType.CLEMCropFileName)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = new List <string>(); Simulation clemParent = Apsim.Parent(this.model, typeof(Simulation)) as Simulation; // get crop file names fieldNames.AddRange(Apsim.ChildrenRecursively(clemParent, typeof(FileCrop)).Select(a => a.Name).ToList()); fieldNames.AddRange(Apsim.ChildrenRecursively(clemParent, typeof(FileSQLiteCrop)).Select(a => a.Name).ToList()); if (fieldNames.Count != 0) { cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && (properties[i].Display.Type == DisplayType.CLEMGraspFileName)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = new List <string>(); Simulation clemParent = Apsim.Parent(this.model, typeof(Simulation)) as Simulation; // get GRASP file names fieldNames.AddRange(Apsim.ChildrenRecursively(clemParent, typeof(FileGRASP)).Select(a => a.Name).ToList()); fieldNames.AddRange(Apsim.ChildrenRecursively(clemParent, typeof(FileSQLiteGRASP)).Select(a => a.Name).ToList()); if (fieldNames.Count != 0) { cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.Model) { cell.EditorType = EditorTypeEnum.DropDown; string[] modelNames = GetModelNames(properties[i].Display.ModelType); if (modelNames != null) { cell.DropDownStrings = modelNames; } } else { object cellValue = properties[i].ValueWithArrayHandling; if (cellValue is DateTime) { cell.EditorType = EditorTypeEnum.DateTime; } else if (cellValue is bool) { cell.EditorType = EditorTypeEnum.Boolean; } else if (cellValue.GetType().IsEnum) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = VariableProperty.EnumToStrings(cellValue); Enum cellValueAsEnum = cellValue as Enum; if (cellValueAsEnum != null) { cell.Value = VariableProperty.GetEnumDescription(cellValueAsEnum); } } else if (cellValue.GetType() == typeof(IPlant)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> cropNames = new List <string>(); foreach (Model crop in Apsim.FindAll(model, typeof(IPlant))) { cropNames.Add(crop.Name); } cell.DropDownStrings = cropNames.ToArray(); } else if (properties[i].DataType == typeof(IPlant)) { List <string> plantNames = Apsim.FindAll(model, typeof(IPlant)).Select(m => m.Name).ToList(); cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = plantNames.ToArray(); } else if (!string.IsNullOrWhiteSpace(properties[i].Display?.Values)) { MethodInfo method = model.GetType().GetMethod(properties[i].Display.Values); string[] values = ((IEnumerable <object>)method.Invoke(model, null))?.Select(v => v?.ToString())?.ToArray(); cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = values; } else { cell.EditorType = EditorTypeEnum.TextBox; } } } IGridColumn descriptionColumn = grid.GetColumn(0); descriptionColumn.Width = -1; descriptionColumn.ReadOnly = true; IGridColumn valueColumn = grid.GetColumn(1); valueColumn.Width = -1; }
/// <summary> /// Format the grid. /// </summary> protected virtual void FormatGrid() { for (int i = 0; i < properties.Count; i++) { IGridCell cell = grid.GetCell(1, i); if (properties[i] is VariableObject) { cell.EditorType = EditorTypeEnum.TextBox; grid.SetRowAsSeparator(i, true); } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.TableName) { cell.EditorType = EditorTypeEnum.DropDown; if (storage == null) { storage = model.FindInScope <IDataStore>(); } cell.DropDownStrings = storage.Reader.TableNames.ToArray(); } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.CultivarName) { cell.EditorType = EditorTypeEnum.DropDown; IPlant crop; if (properties[i].Display.PlantName != null) { crop = model.FindInScope <IPlant>(properties[i].Display.PlantName); } else { crop = GetCrop(properties); } if (crop != null) { cell.DropDownStrings = PropertyPresenterHelpers.GetCultivarNames(crop); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.LifeCycleName) { cell.EditorType = EditorTypeEnum.DropDown; Zone zone = model.FindInScope <Zone>(); if (zone != null) { cell.DropDownStrings = PropertyPresenterHelpers.GetLifeCycleNames(zone); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.LifePhaseName) { cell.EditorType = EditorTypeEnum.DropDown; LifeCycle lifeCycle = GetLifeCycle(properties); if (lifeCycle != null) { cell.DropDownStrings = PropertyPresenterHelpers.GetPhaseNames(lifeCycle); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.FileName) { cell.EditorType = EditorTypeEnum.Button; } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.FileNames) { cell.EditorType = EditorTypeEnum.MultiFiles; } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.DirectoryName) { cell.EditorType = EditorTypeEnum.DirectoryChooser; } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.FieldName) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = GetFieldNames(); if (fieldNames != null) { fieldNames.Insert(0, string.Empty); cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.ResidueName && model is SurfaceOrganicMatter) { cell.EditorType = EditorTypeEnum.DropDown; string[] fieldNames = GetResidueNames(); if (fieldNames != null) { cell.DropDownStrings = fieldNames; } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.Model) { cell.EditorType = EditorTypeEnum.DropDown; string[] modelNames = GetModelNames(properties[i].Display.ModelType); if (modelNames != null) { cell.DropDownStrings = modelNames; } } else { object cellValue = properties[i].ValueWithArrayHandling; if (cellValue is DateTime) { cell.EditorType = EditorTypeEnum.DateTime; } else if (cellValue is bool) { cell.EditorType = EditorTypeEnum.Boolean; } else if (cellValue.GetType().IsEnum) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = VariableProperty.EnumToStrings(cellValue); Enum cellValueAsEnum = cellValue as Enum; if (cellValueAsEnum != null) { cell.Value = VariableProperty.GetEnumDescription(cellValueAsEnum); } } else if (cellValue.GetType() == typeof(IPlant)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> cropNames = new List <string>(); foreach (Model crop in model.FindAllInScope <IPlant>()) { cropNames.Add(crop.Name); } cell.DropDownStrings = cropNames.ToArray(); } else if (properties[i].DataType == typeof(IPlant)) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = model.FindAllInScope <IPlant>().OfType <IModel>().Select(m => m.Name).ToArray(); } else if (!string.IsNullOrWhiteSpace(properties[i].Display?.Values)) { explorerPresenter.ApsimXFile.Links.Resolve(model, allLinks: true, throwOnFail: false); BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy; MethodInfo method = model.GetType().GetMethod(properties[i].Display.Values, flags); string[] values = ((IEnumerable <object>)method.Invoke(model, properties[i].Display.ValuesArgs))?.Select(v => v?.ToString())?.ToArray(); cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = values; } else { cell.EditorType = EditorTypeEnum.TextBox; } } cell.IsRowReadonly = !IsPropertyEnabled(i); } IGridColumn descriptionColumn = grid.GetColumn(0); descriptionColumn.Width = -1; descriptionColumn.ReadOnly = true; IGridColumn valueColumn = grid.GetColumn(1); valueColumn.Width = -1; }
/// <summary> /// Instantiates a Property object by reading metadata about /// the given property. /// </summary> /// <param name="obj">The object reference used to fetch the current value of the property.</param> /// <param name="metadata">Property metadata.</param> public Property(object obj, PropertyInfo metadata) { IModel model = obj as IModel; ID = Guid.NewGuid(); Name = metadata.GetCustomAttribute <DescriptionAttribute>()?.ToString(); if (string.IsNullOrEmpty(Name)) { Name = metadata.Name; } Tooltip = metadata.GetCustomAttribute <TooltipAttribute>()?.Tooltip; Separators = metadata.GetCustomAttributes <SeparatorAttribute>()?.Select(s => s.ToString())?.ToList(); Value = metadata.GetValue(obj); if (metadata.PropertyType == typeof(DateTime) || (metadata.PropertyType == typeof(DateTime?) && Value != null)) { // Note: ToShortDateString() uses the current culture, which is what we want in this case. Value = ((DateTime)Value).ToShortDateString(); } // ?else if property type isn't a struct? else if (Value != null && typeof(IModel).IsAssignableFrom(Value.GetType())) { Value = ((IModel)Value).Name; } else if (metadata.PropertyType.IsEnum) { Value = VariableProperty.GetEnumDescription((Enum)Enum.Parse(metadata.PropertyType, Value?.ToString())); } else if (metadata.PropertyType != typeof(bool) && metadata.PropertyType != typeof(System.Drawing.Color)) { Value = ReflectionUtilities.ObjectToString(Value, CultureInfo.CurrentCulture); } // fixme - need to fix this unmaintainable mess brought across from the old PropertyPresenter DisplayAttribute attrib = metadata.GetCustomAttribute <DisplayAttribute>(); DisplayType displayType = attrib?.Type ?? DisplayType.None; // For compatibility with the old PropertyPresenter, assume a default of // DisplayType.DropDown if the Values property is specified. if (displayType == DisplayType.None && !string.IsNullOrEmpty(attrib?.Values)) { displayType = DisplayType.DropDown; } switch (displayType) { case DisplayType.None: if (metadata.PropertyType.IsEnum) { // Enums use dropdown DropDownOptions = Enum.GetValues(metadata.PropertyType).Cast <Enum>() .Select(e => VariableProperty.GetEnumDescription(e)) .ToArray(); DisplayMethod = PropertyType.DropDown; } else if (typeof(IModel).IsAssignableFrom(metadata.PropertyType)) { // Model selector - use a dropdown containing names of all models in scope. DisplayMethod = PropertyType.DropDown; DropDownOptions = model.FindAllInScope() .Where(m => metadata.PropertyType.IsAssignableFrom(m.GetType())) .Select(m => m.Name) .ToArray(); } else if (metadata.PropertyType == typeof(bool)) { DisplayMethod = PropertyType.Checkbox; } else if (metadata.PropertyType == typeof(System.Drawing.Color)) { DisplayMethod = PropertyType.Colour; } else { DisplayMethod = PropertyType.SingleLineText; } break; case DisplayType.FileName: DisplayMethod = PropertyType.File; break; case DisplayType.FileNames: DisplayMethod = PropertyType.Files; break; case DisplayType.DirectoryName: DisplayMethod = PropertyType.Directory; break; case DisplayType.DropDown: string methodName = metadata.GetCustomAttribute <DisplayAttribute>().Values; if (methodName == null) { throw new ArgumentNullException($"When using DisplayType.DropDown, the Values property must be specified."); } BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy; MethodInfo method = model.GetType().GetMethod(methodName, flags); object[] args = metadata.GetCustomAttribute <DisplayAttribute>().ValuesArgs; // Attempt to resolve links - populating the dropdown may // require access to linked models. Simulations sims = model.FindAncestor <Simulations>(); if (sims != null) { sims.Links.Resolve(model, allLinks: true, throwOnFail: false); } DropDownOptions = ((IEnumerable <object>)method.Invoke(model, args))?.Select(v => v?.ToString())?.ToArray(); DisplayMethod = PropertyType.DropDown; break; case DisplayType.CultivarName: DisplayMethod = PropertyType.DropDown; IPlant plant = null; PropertyInfo plantProperty = model.GetType().GetProperties().FirstOrDefault(p => typeof(IPlant).IsAssignableFrom(p.PropertyType)); if (plantProperty != null) { plant = plantProperty.GetValue(model) as IPlant; } else { plant = model.FindInScope <IPlant>(); } if (plant != null) { DropDownOptions = PropertyPresenterHelpers.GetCultivarNames(plant); } break; case DisplayType.TableName: DisplayMethod = PropertyType.DropDown; DropDownOptions = model.FindInScope <IDataStore>()?.Reader?.TableNames?.ToArray(); break; case DisplayType.FieldName: DisplayMethod = PropertyType.DropDown; IDataStore storage = model.FindInScope <IDataStore>(); PropertyInfo tableNameProperty = model.GetType().GetProperties().FirstOrDefault(p => p.GetCustomAttribute <DisplayAttribute>()?.Type == DisplayType.TableName); string tableName = tableNameProperty?.GetValue(model) as string; if (storage != null && storage.Reader.TableNames.Contains(tableName)) { DropDownOptions = storage.Reader.ColumnNames(tableName).ToArray(); } break; case DisplayType.LifeCycleName: DisplayMethod = PropertyType.DropDown; Zone zone = model.FindInScope <Zone>(); if (zone != null) { DropDownOptions = PropertyPresenterHelpers.GetLifeCycleNames(zone); } break; case DisplayType.LifePhaseName: DisplayMethod = PropertyType.DropDown; LifeCycle lifeCycle = null; if (attrib.LifeCycleName != null) { lifeCycle = model.FindInScope <LifeCycle>(attrib.LifeCycleName); } else { foreach (PropertyInfo property in model.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (property.PropertyType == typeof(string)) { string value = property.GetValue(model) as string; LifeCycle match = model.FindInScope <LifeCycle>(value); if (match != null) { lifeCycle = match; break; } } } } if (lifeCycle != null) { DropDownOptions = PropertyPresenterHelpers.GetPhaseNames(lifeCycle).ToArray(); } break; case DisplayType.Model: DisplayMethod = PropertyType.DropDown; DropDownOptions = model.FindAllInScope().Where(m => metadata.PropertyType.IsAssignableFrom(m.GetType())) .Select(m => m.Name) .ToArray(); break; case DisplayType.ResidueName: if (model is SurfaceOrganicMatter surfaceOM) { DisplayMethod = PropertyType.DropDown; DropDownOptions = surfaceOM.ResidueTypeNames().ToArray(); break; } else { throw new NotImplementedException($"Display type {displayType} is only supported on models of type {typeof(SurfaceOrganicMatter).Name}, but model is of type {model.GetType().Name}."); } case DisplayType.MultiLineText: DisplayMethod = PropertyType.MultiLineText; if (Value is IEnumerable enumerable && metadata.PropertyType != typeof(string)) { Value = string.Join(Environment.NewLine, ((IEnumerable)metadata.GetValue(obj)).ToGenericEnumerable()); } break; // Should never happen - presenter should handle this(?) //case DisplayType.SubModel: default: throw new NotImplementedException($"Unknown display type {displayType}"); } // If the list of dropdown options doesn't contain the actual value of the // property, add that value to the list of valid options. if (DisplayMethod == PropertyType.DropDown && Value != null) { if (DropDownOptions == null) { DropDownOptions = new string[1] { Value.ToString() } } ; else if (!DropDownOptions.Contains(Value.ToString())) { List <string> values = DropDownOptions.ToList(); values.Add(Value.ToString()); DropDownOptions = values.ToArray(); } } }
/// <summary> /// Format the grid. /// </summary> private void FormatGrid() { for (int i = 0; i < this.properties.Count; i++) { IGridCell cell = this.grid.GetCell(1, i); if (this.properties[i] is VariableObject) { cell.EditorType = EditorTypeEnum.TextBox; grid.SetRowAsSeparator(i, true); } else if (this.properties[i].Display != null && this.properties[i].Display.Type == DisplayType.TableName) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = this.storage.TableNames.ToArray(); } else if (this.properties[i].Display != null && this.properties[i].Display.Type == DisplayType.CultivarName) { cell.EditorType = EditorTypeEnum.DropDown; IPlant crop = this.GetCrop(this.properties); if (crop != null) { cell.DropDownStrings = this.GetCultivarNames(crop); } } else if (this.properties[i].Display != null && this.properties[i].Display.Type == DisplayType.FileName) { cell.EditorType = EditorTypeEnum.Button; } else if (this.properties[i].Display != null && this.properties[i].Display.Type == DisplayType.FieldName) { cell.EditorType = EditorTypeEnum.DropDown; string[] fieldNames = this.GetFieldNames(); if (fieldNames != null) { cell.DropDownStrings = fieldNames; } } else if (this.properties[i].Display != null && this.properties[i].Display.Type == DisplayType.ResidueName && this.model is Models.Surface.SurfaceOrganicMatter) { cell.EditorType = EditorTypeEnum.DropDown; string[] fieldNames = GetResidueNames(); if (fieldNames != null) { cell.DropDownStrings = fieldNames; } } else if (this.properties[i].Display != null && properties[i].Display.Type == DisplayType.Model) { cell.EditorType = EditorTypeEnum.DropDown; string[] modelNames = GetModelNames(properties[i].Display.ModelType); if (modelNames != null) { cell.DropDownStrings = modelNames; } } else { object cellValue = this.properties[i].ValueWithArrayHandling; if (cellValue is DateTime) { cell.EditorType = EditorTypeEnum.DateTime; } else if (cellValue is bool) { cell.EditorType = EditorTypeEnum.Boolean; } else if (cellValue.GetType().IsEnum) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = VariableProperty.EnumToStrings(cellValue); Enum cellValueAsEnum = cellValue as Enum; if (cellValueAsEnum != null) { cell.Value = VariableProperty.GetEnumDescription(cellValueAsEnum); } } else if (cellValue.GetType() == typeof(IPlant)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> cropNames = new List <string>(); foreach (Model crop in Apsim.FindAll(this.model, typeof(IPlant))) { cropNames.Add(crop.Name); } cell.DropDownStrings = cropNames.ToArray(); } else if (this.properties[i].DataType == typeof(IPlant)) { List <string> plantNames = Apsim.FindAll(this.model, typeof(IPlant)).Select(m => m.Name).ToList(); cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = plantNames.ToArray(); } else { cell.EditorType = EditorTypeEnum.TextBox; } } } IGridColumn descriptionColumn = this.grid.GetColumn(0); descriptionColumn.Width = -1; descriptionColumn.ReadOnly = true; IGridColumn valueColumn = this.grid.GetColumn(1); valueColumn.Width = -1; }
/// <summary> /// Instantiates a Property object by reading metadata about /// the given property. /// </summary> /// <param name="obj">The object reference used to fetch the current value of the property.</param> /// <param name="metadata">Property metadata.</param> public Property(object obj, PropertyInfo metadata) { IModel model = obj as IModel; ID = Guid.NewGuid(); Name = metadata.GetCustomAttribute <DescriptionAttribute>()?.ToString(); if (string.IsNullOrEmpty(Name)) { Name = metadata.Name; } Tooltip = metadata.GetCustomAttribute <TooltipAttribute>()?.Tooltip; Separators = metadata.GetCustomAttributes <SeparatorAttribute>()?.Select(s => s.ToString())?.ToList(); Value = metadata.GetValue(obj); if (metadata.PropertyType == typeof(DateTime) || (metadata.PropertyType == typeof(DateTime?) && Value != null)) { // Note: ToShortDateString() uses the current culture, which is what we want in this case. Value = ((DateTime)Value).ToShortDateString(); } // ?else if property type isn't a struct? else if (Value != null && typeof(IModel).IsAssignableFrom(Value.GetType())) { Value = ((IModel)Value).Name; } else if (metadata.PropertyType != typeof(bool) && metadata.PropertyType != typeof(System.Drawing.Color)) { Value = ReflectionUtilities.ObjectToString(Value, CultureInfo.CurrentCulture); } // fixme - need to fix this unmaintainable mess brought across from the old PropertyPresenter DisplayAttribute attrib = metadata.GetCustomAttribute <DisplayAttribute>(); DisplayType displayType = attrib?.Type ?? DisplayType.None; switch (displayType) { case DisplayType.None: if (metadata.PropertyType.IsEnum) { // Enums use dropdown DropDownOptions = Enum.GetValues(metadata.PropertyType).Cast <Enum>() .Select(e => VariableProperty.GetEnumDescription(e)) .ToArray(); DisplayMethod = PropertyType.DropDown; } else if (typeof(IModel).IsAssignableFrom(metadata.PropertyType)) { // Model selector - use a dropdown containing names of all models in scope. DisplayMethod = PropertyType.DropDown; DropDownOptions = model.FindAllInScope() .Where(m => metadata.PropertyType.IsAssignableFrom(m.GetType())) .Select(m => m.Name) .ToArray(); } else if (metadata.PropertyType == typeof(bool)) { DisplayMethod = PropertyType.Checkbox; } else if (metadata.PropertyType == typeof(System.Drawing.Color)) { DisplayMethod = PropertyType.Colour; } else { DisplayMethod = PropertyType.SingleLineText; } break; case DisplayType.FileName: DisplayMethod = PropertyType.File; break; case DisplayType.FileNames: DisplayMethod = PropertyType.Files; break; case DisplayType.DirectoryName: DisplayMethod = PropertyType.Directory; break; case DisplayType.DropDown: string methodName = metadata.GetCustomAttribute <DisplayAttribute>().Values; MethodInfo method = model.GetType().GetMethod(methodName); DropDownOptions = ((IEnumerable <object>)method.Invoke(model, null))?.Select(v => v?.ToString())?.ToArray(); DisplayMethod = PropertyType.DropDown; break; case DisplayType.CultivarName: DisplayMethod = PropertyType.DropDown; IPlant plant = null; PropertyInfo plantProperty = model.GetType().GetProperties().FirstOrDefault(p => typeof(IPlant).IsAssignableFrom(p.PropertyType)); if (plantProperty != null) { plant = plantProperty.GetValue(model) as IPlant; } else { plant = model.FindInScope <IPlant>(); } DropDownOptions = PropertyPresenterHelpers.GetCultivarNames(plant); break; case DisplayType.TableName: DisplayMethod = PropertyType.DropDown; DropDownOptions = model.FindInScope <IDataStore>()?.Reader?.TableNames?.ToArray(); break; case DisplayType.FieldName: DisplayMethod = PropertyType.DropDown; IDataStore storage = model.FindInScope <IDataStore>(); PropertyInfo tableNameProperty = model.GetType().GetProperties().FirstOrDefault(p => p.GetCustomAttribute <DisplayAttribute>()?.Type == DisplayType.TableName); string tableName = tableNameProperty?.GetValue(model) as string; if (storage != null && storage.Reader.TableNames.Contains(tableName)) { DropDownOptions = storage.Reader.ColumnNames(tableName).ToArray(); } break; case DisplayType.LifeCycleName: DisplayMethod = PropertyType.DropDown; Zone zone = model.FindInScope <Zone>(); if (zone != null) { DropDownOptions = PropertyPresenterHelpers.GetLifeCycleNames(zone); } break; case DisplayType.LifePhaseName: DisplayMethod = PropertyType.DropDown; LifeCycle lifeCycle = null; if (attrib.LifeCycleName != null) { lifeCycle = model.FindInScope <LifeCycle>(attrib.LifeCycleName); } else { foreach (PropertyInfo property in model.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (property.PropertyType == typeof(string)) { string value = property.GetValue(model) as string; LifeCycle match = model.FindInScope <LifeCycle>(value); if (match != null) { lifeCycle = match; break; } } } } if (lifeCycle != null) { DropDownOptions = PropertyPresenterHelpers.GetPhaseNames(lifeCycle).ToArray(); } break; case DisplayType.Model: DisplayMethod = PropertyType.DropDown; DropDownOptions = model.FindAllInScope().Where(m => metadata.PropertyType.IsAssignableFrom(m.GetType())) .Select(m => m.Name) .ToArray(); break; case DisplayType.ResidueName: if (model is SurfaceOrganicMatter surfaceOM) { DisplayMethod = PropertyType.DropDown; DropDownOptions = surfaceOM.ResidueTypeNames().ToArray(); break; } else { throw new NotImplementedException($"Display type {displayType} is only supported on models of type {typeof(SurfaceOrganicMatter).Name}, but model is of type {model.GetType().Name}."); } // Should never happen - presenter should handle this(?) //case DisplayType.SubModel: default: throw new NotImplementedException($"Unknown display type {displayType}"); } }
/// <summary> /// Format the grid. /// </summary> protected virtual void FormatGrid() { for (int i = 0; i < properties.Count; i++) { IGridCell cell = grid.GetCell(1, i); if (properties[i] is VariableObject) { cell.EditorType = EditorTypeEnum.TextBox; grid.SetRowAsSeparator(i, true); } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.TableName) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = storage.Reader.TableNames.ToArray(); } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.CultivarName) { cell.EditorType = EditorTypeEnum.DropDown; IPlant crop; if (properties[i].Display.PlantName != null) { crop = model.FindInScope <IPlant>(properties[i].Display.PlantName); } else { crop = GetCrop(properties); } if (crop != null) { cell.DropDownStrings = PropertyPresenterHelpers.GetCultivarNames(crop); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.LifeCycleName) { cell.EditorType = EditorTypeEnum.DropDown; Zone zone = model.FindInScope <Zone>(); if (zone != null) { cell.DropDownStrings = PropertyPresenterHelpers.GetLifeCycleNames(zone); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.LifePhaseName) { cell.EditorType = EditorTypeEnum.DropDown; LifeCycle lifeCycle = GetLifeCycle(properties); if (lifeCycle != null) { cell.DropDownStrings = PropertyPresenterHelpers.GetPhaseNames(lifeCycle); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.FileName) { cell.EditorType = EditorTypeEnum.Button; } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.FileNames) { cell.EditorType = EditorTypeEnum.MultiFiles; } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.DirectoryName) { cell.EditorType = EditorTypeEnum.DirectoryChooser; } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.FieldName) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = GetFieldNames(); if (fieldNames != null) { fieldNames.Insert(0, string.Empty); cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.ResidueName && model is SurfaceOrganicMatter) { cell.EditorType = EditorTypeEnum.DropDown; string[] fieldNames = GetResidueNames(); if (fieldNames != null) { cell.DropDownStrings = fieldNames; } } else if (properties[i].Display != null && (properties[i].Display.Type == DisplayType.CLEMResource)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = new List <string>(); fieldNames.AddRange(this.GetCLEMResourceNames(this.properties[i].Display.CLEMResourceGroups)); // add any extras elements provided to the list. if (this.properties[i].Display.CLEMExtraEntries != null) { fieldNames.AddRange(this.properties[i].Display.CLEMExtraEntries); } if (fieldNames.Count != 0) { cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && (properties[i].Display.Type == DisplayType.CLEMCropFileReader)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = new List <string>(); Simulation clemParent = model.FindAncestor <Simulation>(); // get crop file names fieldNames.AddRange(clemParent.FindAllDescendants <FileCrop>().Select(a => a.Name).ToList()); fieldNames.AddRange(clemParent.FindAllDescendants <FileSQLiteCrop>().Select(a => a.Name).ToList()); if (fieldNames.Count != 0) { cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && (properties[i].Display.Type == DisplayType.CLEMPastureFileReader)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> fieldNames = new List <string>(); Simulation clemParent = model.FindAncestor <Simulation>(); // get Pasture file names fieldNames.AddRange(clemParent.FindAllDescendants <FilePasture>().Select(a => a.Name).ToList()); fieldNames.AddRange(clemParent.FindAllDescendants <FileSQLitePasture>().Select(a => a.Name).ToList()); if (fieldNames.Count != 0) { cell.DropDownStrings = fieldNames.ToArray(); } } else if (properties[i].Display != null && properties[i].Display.Type == DisplayType.Model) { cell.EditorType = EditorTypeEnum.DropDown; string[] modelNames = GetModelNames(properties[i].Display.ModelType); if (modelNames != null) { cell.DropDownStrings = modelNames; } } else { object cellValue = properties[i].ValueWithArrayHandling; if (cellValue is DateTime) { cell.EditorType = EditorTypeEnum.DateTime; } else if (cellValue is bool) { cell.EditorType = EditorTypeEnum.Boolean; } else if (cellValue.GetType().IsEnum) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = VariableProperty.EnumToStrings(cellValue); Enum cellValueAsEnum = cellValue as Enum; if (cellValueAsEnum != null) { cell.Value = VariableProperty.GetEnumDescription(cellValueAsEnum); } } else if (cellValue.GetType() == typeof(IPlant)) { cell.EditorType = EditorTypeEnum.DropDown; List <string> cropNames = new List <string>(); foreach (Model crop in model.FindAllInScope <IPlant>()) { cropNames.Add(crop.Name); } cell.DropDownStrings = cropNames.ToArray(); } else if (properties[i].DataType == typeof(IPlant)) { cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = model.FindAllInScope <IPlant>().OfType <IModel>().Select(m => m.Name).ToArray(); } else if (!string.IsNullOrWhiteSpace(properties[i].Display?.Values)) { explorerPresenter.ApsimXFile.Links.Resolve(model, allLinks: true, throwOnFail: false); MethodInfo method = model.GetType().GetMethod(properties[i].Display.Values); string[] values = ((IEnumerable <object>)method.Invoke(model, null))?.Select(v => v?.ToString())?.ToArray(); cell.EditorType = EditorTypeEnum.DropDown; cell.DropDownStrings = values; } else { cell.EditorType = EditorTypeEnum.TextBox; } } cell.IsRowReadonly = !IsPropertyEnabled(i); } IGridColumn descriptionColumn = grid.GetColumn(0); descriptionColumn.Width = -1; descriptionColumn.ReadOnly = true; IGridColumn valueColumn = grid.GetColumn(1); valueColumn.Width = -1; }