I have a custom collection, lets says COL, that derives from ObjectModel.Collection.
I have my own collection editor that works fine when a property, of type COL, is Read and Write enabled.
However, if I change the property to ReadOnly, the open editor button stops showing in the property grid.
As a test, I override my custom editor with the CollectionEditor, and that worked fine.
So, my question is, what check is the property grid making, that CollectionEditor passes but my collection editor fails?
There's not much to override in UITypeEditor, so I fear there's some hard coding going on with regards to CollectionEditor.
Cheers.
ETA:
I've answered the question below.
I've found out why it wasn't appearing and it appears to be a bug in the PropertyGrid.
The button does appear if the EditStyle is set to Modal, but does not appear if it's set to DropDown.
One would have thought that the styles were just for ..., well, style?
Looking in reflector, the issue occurs because additional checks of readonly propertyies (such as checking if it's a reference type - and enabling the button) only happen if the style is set to modal. Hmmm, nice one.
I think the behavior you describe in your own answer is not a bug and is by design, and frankly it's quite logical. Look the dropdown editors around: they offer the user to make a selection and therefore the result is a new value for the property (see ColorEditor, AnchorEditor, DockEditor, CursorEditor, ...). As such the property must not be readonly. A modal editor is more to edit a value (usually a reference type) and therefore can be used even if the property is readonly. Of course, this one can also be used to select a new value (like the FileNameEditor for example).
So maybe the answer is "look at your UI design". Are you sure you should use a dropdown editor to edit the content of a reference type?
Related
This is my first question here so bear with me.
I've encountered a bug in my software where multiple radio buttons in a given control are both capable of being selected simultanously as shown here. Note that this is a very large program.
Multiple Radio Buttons Selected in One Control
While debugging this I found the cause of this behavior is that the ReadOnly attribute on the "Checked" property for the RadioButton class is set to true. In additional, it seems as though the ReadOnly attribute for all properties on the RadioButton are set to True. I was able to confirm this was causing the unexpected behavior by changing the value of the ReadOnly attribute via reflection back to false. Interestingly, when this occured every radio button in my application experiences the same problem.
I was able to track down at what point the attributes changes. In a different form I show some objects on a property grid, when I set the SelectedObject of that property grid to the object thats when all of the attributes change. The form with the property grid and the form with the radio button are not common in anyway.
I've been unable to track down the source of this problem for some time now. If anyone has any ideas as to how this is possible it may be able to help me figure out where the issue is in my program.
Here are pictures of my debug window showing the ReadOnly attribute value on a radiobutton just before and just after I set the SelectedObject property for the property grid
Just Before
Just After
Thank you.
Edit 1:
Here is the code that sets the property grid. Line 179. While debugging there are 3 different sub property objects that need to be shown in different tabs. The first causes no issues. The second does.
Property Grid Code
Edit 2:
Solution
Thanks to all the people who commented. I was actually able to figure out the solution to the problem with help from the following thread.
Setting ReadOnly Property in PropertyGrid
Basically, there was a spot in my code where I was setting the ReadOnly status of all attributes in an object to True. It seems as though there is some kind of common "ReadOnly" attribute that is shared among all objects if they do not have a special define.
My original solution was to first check if the ReadOnlyAttribute existed before setting its value like so.
Original Solution
However this was returning True for properties defined like this
Non ReadOnly Property
But False for properties like this
ReadOnly Property
My guess again is that there is some kind of inherit attribute that was getting found.
My solution therefore was to check with the following function.
Final Solution
Putting this check in my SetReadOnly() function solved my problem.
Solution
Thanks to all the people who commented. I was actually able to figure out the solution to the problem with help from the following thread.
Setting ReadOnly Property in PropertyGrid
Basically, there was a spot in my code where I was setting the ReadOnly status of all attributes in an object to True. It seems as though there is some kind of common "ReadOnly" attribute that is shared among all objects if they do not have a special define.
My original solution was to first check if the ReadOnlyAttribute existed before setting its value like so.
Original Solution
However this was returning True for properties defined like this
Non ReadOnly Property
But False for properties like this
ReadOnly Property
My guess again is that there is some kind of inherit attribute that was getting found.
My solution therefore was to check with the following function.
Final Solution
Putting this check in my SetReadOnly() function solved my problem.
I have a need to create a custom version of the UWP DataGridNumericalColumn that allows customization of the RadNumericBox properties (ValueFormat, ButtonsVisibility, SmallChange, LargeChange, Value) as well as the ability to edit the value as cents (199) without decimal place while editing, but display as normal dollars with decimal cents (1.99) while not editing. I've tried two different approaches to extend existing controls, neither of which I can seem to get to work fully for me.
1) Tried deriving from DataGridNumericalColumn - impossible due to inaccessible internal members down the line, even with full source code from GitHub available.
2) Tried deriving from DataGridTemplateColumn - somewhat workable for initial display, but everything relating to inline edit mode vs display mode and validation message display on the cell seems beyond reach (not override-able) and I can't seem to use CellContentTemplateSelector to choose between the inline Edit mode RadNumericBox display and the normal TextBlock display because I can't seem to detect when Edit mode is applied to the cell.
It is starting to seem like the only way I can achieve what I need is to fork the GitHub code-base so I can derive from DataGridNumericalColumn with access to internal code.
What approach could I take to achieve my desired customizations?
(I am using Telerik UI For Universal Windows Platform, version 2017.1.301.45, at the time of this writing.)
I eventually worked out workarounds that let me get past the main difficulties in extending this functionality using the derive from DataGridTemplateColumn approach. Here are the updates and customizations I made - they are described mostly at the conceptual level, but it should be enough for others to duplicate this sort of customization for themselves.
UPDATE1:
An update as I've been working on this:
Continuing along with the approach of deriving from DataGridTemplateColumn, I found that I can successfully change my displayed markup for edit mode vs display mode by creating custom commands for editing operations in the grid (CustomBeginEditCommand, CustomCancelEditCommand, and CustomCommitEditCommand very similar to the ones in http://docs.telerik.com/devtools/universal-windows-platform/controls/raddatagrid/features/commands/editing-commands/datagrid-editingcommands-begineditcommand ) along with an interface IItemAwareOfEditMode, applied to the ViewModel items for the Grid's data, that has a single bool property IsInEditMode that I set to true or false appropriately in the custom commands, which is then used in a custom DataTemplateSelector to decide when to apply my edit markup vs my display markup. This uses (DataTemplate)XamlReader.LoadWithInitialTemplateValidation(editControlMarkup) for translating dynamically created markup strings to DataTemplates. For my implementation, I create the markup in a PropertyChangedCallback for my custom column's PropertyNameProperty dependency property.
However, I still am having issues with validation and displaying the validation messages, and reverting values when the user cancels edit. I have the ViewModel for the grid row items implemented such that they derive from ValidateViewModelBase, and so they add/remove errors appropriately according to the documentation on validation at http://docs.telerik.com/devtools/universal-windows-platform/controls/raddatagrid/features/validation . If I use the DataGridNumericalColumn (not customized) with the same data, the validation messages do appear pointing to the cell when the data is invalid, but with my custom column, HasErrors is true on the items, but the validation messages don't appear. Looking at the validation code in https://github.com/telerik/UI-For-UWP/blob/master/Controls/Grid/Grid.UWP/View/Columns/TypedColumns/DataGridTypedColumn.cs in the CreateEditorContainer function, it seems there is an EditRowHostPanel and ValidationControl involved along with the editor content, but I don't have access to pieces needed to implement the container exactly as is done there.
What can I do to get the validation messages to appear as they do in the DataGridNumericalColumn?
Also, what can I do to make cancelling an edit (clicking the blue X for the row when in edit mode) actually revert my custom column's value to what it was prior to entering edit mode?
UPDATE2:
Another update as I've been working on this:
Continuing along with the approach of deriving from DataGridTemplateColumn, I've managed to successfully display validation messages for the edit mode by including a ValidationControl in the edit mode template markup, which references the RadNumericBox from the template (by Name) using the ControlPeer property, and giving its DataItem property a value of "{Binding}", and appropriately populating its PropertyName.
This is getting close to what I need, but it seems that my CustomCancelEditCommand, which uses
Owner.CommandService.ExecuteDefaultCommand(CommandId.CancelEdit, context);
, does not appropriately update the display of the cell to its previous value. It correctly doesn't call the CustomCommitEditCommand when the inline row edit is cancelled; however, it displays as the modified value (not reverted to the value prior to edit). Even if you edit again, the value remains as the modified value when displayed in the grid.
I see that in https://github.com/telerik/UI-For-UWP/blob/master/Controls/Grid/Grid.UWP/View/Services/Commands/Editing/CancelEditCommand.cs in the Execute method, that it executes its base implementation, followed by
Owner.editService.CancelEdit(context.TriggerAction)
, which I don't understand (RadDataGrid does not contain a definition for editService and I can't derive from that CancelEditCommand class because it is internal).
What can I do to make cancelling an edit (clicking the blue X for the row when in edit mode) actually revert my custom column's value to what it was prior to entering edit mode?
UPDATE3:
I have finally managed an EXTENSIVE workaround that does revert my custom columns' value on cancel.
My workaround for the cancel functionality involved:
1) Created a CustomRadDataGrid, which derives from RadDataGrid.
2) Gave my CustomRadDataGrid class a CustomEditingService property, which is a CustomEditingService, which is copied and modified code from EditingService (mostly commenting out unneeded parts, but also changing InitializeEditOperation's implementation and changing CancelEdit to have an out parameter of the operation's OriginalValues dictionary), and which derives from CustomServiceBase<RadDataGrid>, which is copied and modified code from ServiceBase (changed IsOperational to return Owner.DataContext != null), which derives from CustomAttachableObject<T> where T : RadControl, which is copied code from AttachableObject.
3) Added GetActualValueForInstance function and SetActualValueForInstance method to my custom column, which uses reflection to get/set the data row instance's value for this column (based on using my PropertyName Dependency Property's value), and made the InitializeEditOperation of my CustomEditingService just save original values of my custom columns, and made the CancelEdit of my CustomEditingService return that dictionary of original values in an out variable.
4) Made my CustomBeginEditCommand call BeginEdit on the grid's CustomEditingService after calling Owner.CommandService.ExecuteDefaultCommand(CommandId.BeginEdit, context) - that allows my custom column original values to be stored.
5) Made my CustomCommitEditCommand call CommitEdit on the grid's CustomEditingService after calling Owner.CommandService.ExecuteDefaultCommand(CommandId.CommitEdit, context) - that allows my custom editing service to properly track its editing state.
6) Made my CustomCancelEditCommand call CancelEdit on the grid's CustomEditingService AND for each original value dictionary item, use the Key (column, as my custom column) SetActualValueForInstance passing in context.CellInfo.Item and the Value (previously stored original value), BEFORE calling Owner.CommandService.ExecuteDefaultCommand(CommandId.CancelEdit, context) - that restores my custom column original values prior to the standard cancel actions occurring.
Done! Whew... It seems that this library needs a lot of changes to allow for better extend-ability. This has been logged as a feature request at Telerik according to a reply to my support ticket with them on this subject.
I think other people will want to be able to extend Telerik's various DataGridColumn controls too, so I shared my struggle and (eventually) successful customization here.
I am working on a winforms app with a DataGridView control on it, and I am experiencing some frustrating things.
First off, I want to turn off AutoColumnGeneration, but it's not listed in the properties. No problem, I can do that with a line of code...and this is where it gets weird:
In my code, the DataGridView is inaccessible. Its like it doesnt exist on the form. Looking into this, its because the designer is declaring it as part of the InitializeComponent() method instead of where it initializes all the other controls.
Because its in the designer, any change I make there gets reversed so I can't fix this.
Is there any way to stop visual studio from doing this? I found a hack around it by using one of the datagrid columns (which ARE accessible) to create a reference to the datagridview its associated with and access it that way. It works, but its ugly and not intuitive at all.
I think I found it:
In the designer, click on the DataGridView control, and change the property of GenerateMember to true. I'm guessing it is set to false.
That property is used to do just that: hide the control from the code windows. It's useful for Labels or ToolStripSeparators that you don't need to deal with in code.
I personally use the binding source as the datasource which can even be an object and then under columns it will list all of the available columns and you can pick and choose which ones are visible as well as a slew of other options including formatting.
Click the drop down on the datasource and Add a new data source and select the necessary object, in my case an order detail object. Here is some of my designer code which is created by VS2010
this.dgvOrderDetails.DataSource = this.orderDetailBindingSource;
this.orderDetailBindingSource.DataSource = typeof(OrderDetail);
And the binding source code that sets up the data to fill the datagridview (I coded this part)
orderDetailBindingSource.DataSource = orderDetList;
Then just click the ellipses on the Columns property of the datagridview and it will have all the columns listed that are available from the object and I set the ones I want visible, the order, format etc.
As for the 2nd issue I don't think you'll have that problem once you use the designer to make the changes I listed above.
In my case, I declared a private property in the Form's partial class (the file for my code, not the Designer's file) to return the control by navigating through the Controls hierarchy.
private DataGridView MyGrid
{
get { return (DataGridView)this.Controls[0].Controls[1].Controls[0].Controls[1].Controls[0]; }
}
I agree, there ought to be a better way, such as Visual Studio Designer declaring the control like it does most other controls on the form. In the meantime, this works.
Warning!
If the form's control hierarchy is ever changed, the property's definition will have to be manually updated.
Edit
I have a better solution - at least in Visual Studio 2012.
While in the form Designer, with the DataGridView selected, open its properties and look for the GenerateMember property (under the Design node) and ensure it is set to True. Once set to True, the Designer will declare a member variable for the DataGridView control.
The strange thing is that the default value appears to be True, so I'm curious how it was changed to False? Perhaps I mis-clicked when setting the name?
By the way #LarsTech's answer is the same as this update.
I have an existing custom control library with controls which may contain properties: HeaderStyle, ModalStyle, Collapsable, etc...
In the user interface the program is currently displaying a categorized list of these properties. I am trying to update this code to hide properties they dont normally use. I have a list of properties to hide/show based on button click but I am not sure how I can hide these fields programmatically.
I would like to retain any values entered into the fields before hiding and re-display the values if the fields are shown again.
Here is a property that current exists but would like to be hidden/shown on toggle.
/// <summary>ModalStyle property for control</summary>
[XmlAttribute]
[DefaultValue(Utility.Common.Enumerations.ModalStyle.None)]
[Category(PropertyCategories.Rendering)]
[Description("Modal dialog style")]
public ModalStyle? ModalStyle
{
get { return control.ModalStyleActive; }
set { control.ModalStyle = value; }
}
My original though was to do some variant on #if DEBUG but use my own Conditional however I was unable to find a way to change my conditionals via button/toggle.
Can anyone please help with a solution to my problem? I have 20-30 controls with 20 to 30 properties that would like to be filtered.
I have two suggestions that, while they may not give you the exact functionality desired, will keep your solution much more straight forward.
First:
Since you are the library developer you should just decide what properties you want other developers to have access to though the IDE properties window. If a property is seldom used or not very useful through the IDE then just place the [Browsable(false)] attribute on it.
Second:
If you really want all properties to be visible in the IDE properties window, but want to give individuals a way of hiding the more advanced (or less used) ones, just throw them all in an 'Advanced' category. The user can then simply collapse that category and forget about them.
Also: Take a look at Oliver's answer to this question:
[how-to-show-or-hide-properties-dynamically-in-the-propertygrid]
I'm not sure to understand what you are trying to achieve.
When you use Attributes, those are static to the class. So, in your case, when you toggle a show/hide on an object, it's on an instance of the object. Also, you cannot change an attribute value at run-time.
Maybe you should try an alternate solution like creating a global
map<pair<type of object, property name>, is shown>
and update that accordingly from your editor.
And if you want to use something like a property grid, you will have a problem since it won't check your map, but it can be fixed. You could create a new class at run-time and make it a proxy to your current instance. (check on the net how to achieve that, but it's quite simple. There are 2 possibilities: compile from a string or use the ILGenerator.
Hope this help.
I'm using a custom dynamic class object in a PropertyGrid (too much code to post here). In a nutshell, what it does is allow me to create properties at run-time from arbitrary input (in this case XML but that's beside the point). These properties may be any type, including another custom class so they can be nested indefinitely.
The issue I'm having is that the PropertyGrid sees this class as a Collection, and has the button in the Value column that allows you to open the CollectionEditor. This (among other things) has Add and Remove buttons, both of which will fundamentally break the custom class. Rather than try and make these two complicated systems work together nicely, I'd rather simply disable the CollectionEditor altogether. I have another system which allows you to see the child objects of the class (by setting the PropertyGrid.SelectedObject to the child object) and it works fine. I'd prefer the user not even having the option of seeing the CollectionEditor, as it seems to be not at all compatible with this system.
Ideally, I'd like to just put a button in the Value column that I could capture a click-event and handle myself, but that's optional. I also don't care if I disable the CollectionEditor for just the row(s) in question or for the whole PropertyGrid. I'm not using it either way, so anything which just makes it not accessible would be good.
Have you tried deriving your own CollectionEditor and in there override some methods/events to make its form not appear and just return without doing anything?
You then need to mark your collection type properties with:
[EditorAttribute(typeof(MyCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
This will automatically launch your MyCollectionEditor when clicking on the "..." button for the marked collection property in the PropertyGrid.
You may also need to inherit PropertyDescriptor and override the GetEditor method to return your own CollectionEditor.