This is a migrated thread and some comments may be shown as answers.

Enforce unique content in a column using MVVM

10 Answers 420 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Stefan
Top achievements
Rank 1
Stefan asked on 13 Sep 2018, 08:50 AM

Hi,

Short version of question:
What is the best way to enforce unique value in a column in an MVVM environment?

Long version of question:
I’m trying to enforce a unique value in a column using a custom attribute in the data model. I ended up with a solution very much like the last example in the old thread
https://www.telerik.com/forums/force-validation-on-new-record-insert

Direct link to the example provided by Telerik:
https://www.telerik.com/clientsfiles/375715_328546-TestApp-new.zip

However I see some problem with a solution like that.
At a first glance, the solution seems to work, but if you edit a value in a cell and the new value is the same as another cell in the same column, BOTH these cells would have an error. Not just the cell you edited.

I understand that not all cells can be validated whenever a single cell is edited, but is there any way to configure the RadGridView to “refresh” validations of all visible rows when a validation error is either set or removed?

Steps to visualize the problem.

  • Run the example above
  • When the application is running, reduce the window height (from the bottom) so that the scroll bar is visible and the top visible row shows “last name”=”Rockhead 0” and the bottom visible row shows “Rockhead 4”
  • Change last name “Smith 3” to “Smith 1”
  • Now there is a red border around the cell we edited (as expected) but nothing happens to row 2 (First name=Jane1)
  • Scroll to the bottom and back up to the top
  • Now there is also an error on row 2 (First name = Jane1)
  • Change “Smith 1” back to “Smith 3” (on the row where First name=Jane 3)
  • The error on the cell edited is now removed, BUT the error on row 2 (Jane 1) is still visible even as all values in the Last name column is now unique

 

So is there a way to prevent validation on cells when scrolling and as a result the error on row 2 will never be shown?
OR
Is there a way to force re-validation of all visible cells whenever the error-state changes in a cell?

 

I upgraded the example Project above to use version 2018.3.911, but the problem still exists.

Regards
/Stefan Vestin

10 Answers, 1 is accepted

Sort by
0
Vladimir Stoyanov
Telerik team
answered on 18 Sep 2018, 08:36 AM
Hello Stefan,

Thank you for the detailed explanation and the provided project.

What you have observed is the expected behavior. The cell that is currently edited is throwing a validation error, however the other cell with the same value needs to be recreated in order to throw an error. This happens when the cell is taken outside the viewport and brought back in. 

That said I am currently investigating a possible approach for making the RadGridView validate all cells. I will write back to you once I have more information on the matter. 

Thank you in advance for your understanding.

Regards,
Vladimir Stoyanov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Stefan
Top achievements
Rank 1
answered on 18 Sep 2018, 11:57 AM

Thanks for looking into my problem!
Some kind of possibility to force validation would be great!

However, even if there was a new method like “ValidateAllCells”, you would have to know when to call the method.
Letting my custom defined ValidationAttribute keeping track of error state (is there a transition from no_error to error OR from error to no_error) would require quite a lot of plumbing code.

I do not know if a solution like this would fit the internal design of the RadGridView, but it might give you some inspiration :-)

Add an attribute to the column definitions in the GridView:

ValidationTransitionMode= Default | ForceValidationOfAllVisibleCellsInColumn | ForceValidationOfAllVisibleCells

Default:
No change from existing functionality.

ForceValidationOfAllVisibleCellsInColumn:
Whenever a validation result of a cell changes (from no_error to error OR from error to no_error) all the visible cells in the column will be re-validated. I will be using this option for my detection of non-unique-values in the column.

ForceValidationOfAllVisibleCells:
Whenever a validation result of a cell changes (from no_error to error OR from error to no_error) all the visible cells of the complete grid will be re-validated. To be used by people with more complex validation algorithms depending on values in other columns as well.

Once again, thanks for looking into the problem.
Regards
/Stefan


0
Vladimir Stoyanov
Telerik team
answered on 18 Sep 2018, 02:29 PM
Hello Stefan,

Thank you for the provided suggestions. 

I looked into how the desired behavior can be achieved with the current RadGridView implementation and here is what I can suggest:

Basically, you can use a RowStyleSelector in order to force all of the invalid rows to redraw which would trigger the validation logic. You can introduce an IsValid property on the data object(the Person class in the provided example). Then you can modify this property in the IsValid method of the ValidationAttribute. Depending on that property of the data object, you can return 2 different styles(one of them has to include the template of the GridViewRow in order to force a redraw). You can take a look at the Editiing Control Templates article to see how you can extract the GridViewRow ControlTemplate for the theme that you are using. Please, note that the suggested approach for extracting ControlTemplates is with implicit styles and the NoXaml binaries.

I am attaching the sample project back modified to demonstrate what I have in mind. Please, note that the extracted ControlTemplate for the GridViewRow is for the Fluent theme. If you are using a different theme, you will have to extract the template for that theme. 

I hope that the demonstrated approach would help you in proceeding forward with your requirements. 

I remain at your disposal for any further questions you might have.

Regards,
Vladimir Stoyanov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Stefan
Top achievements
Rank 1
answered on 19 Sep 2018, 09:08 AM

Thanks for the example!

I guess I’m quite lost in the jungle of themes and different kind of Telerik binaries (normal and NoXaml).  (See end of this message for a description of my problems running your example)

Even if I was not able to run your example I think I get the idea.

Before digging into concept of themes, I do have some questions:

I’m no expert in WPF/Xaml, but would it be “good behavior” to copy and modify the standard Telerik xaml-templates?

I see no problem for small applications with a life cycle like “develop-deploy-never maintain”, but for a large application to be run in a large organization where the maintenance of the code most likely will be performed by a separate department in the organization. I can see some concerns when there are upgrades released of the Telerik WPF product.

If I got hundreds of rows of template-xaml with a lot of small modifications made by me, wouldn’t it be a risky business to upgrade to a new version of Telerik WPF? Or will the Upgrade-routine actually merge my modifications into the new version? If there is a major upgrade of the visual structure of your components, would it even be possible to do that?

Another concern would be documentation. If I would do a lot of minor changes to the standard template-xaml, I would like to be able to reference some low level technical documentation with a complete version history about changes in the visual structure. I had a quick look at the Telerik website, but I did not find any information about the internals of the visual structure and historical changes made.

To summarize: As I said, I’m no expert in WPF/Xaml, but performing changes in the standard templates feels almost like modifying generated code. It will cause you problems in the future. Please correct me if I am wrong.

 

My problems compiling/running your example:

I was not able to compile/run the example project.
I installed Telerik back in 2017 and has been upgrading using the Visual Studio menu Telerik- Upgrade Wizard.

Just opening your example project results in a warning. ”The referenced component 'Telerik.Windows.Themes.Fluent' could not be found.”

After performing an upgrade of your example project (Visual Studio menu Telerik-Upgrade Wizard and selecting “2018.3.911 (Dev) [Downloaded]”), there are no more build-errors. However, when running, I end up with a XamlParseException at
<ResourceDictionary Source="/Telerik.Windows.Themes.Fluent;component/Themes/System.Windows.xaml"/>

After the upgrade of your example project, I end up with a lot of dll:s in “lib\RCWPF\2018.3.911.40\”   (e.g. Telerik.Windows.Controls.GridView.dll). However there are no themes assemblies in that folder.

 

Regards
/Stefan

0
Vladimir Stoyanov
Telerik team
answered on 21 Sep 2018, 03:55 PM
Hello Stefan,

Thank you for the update. 

Please, allow me to state a few things which I hope would produce some clarity.

I used the Fluent theme for my example which was introduced in 2018 and that was the reason for the first exception. As for the second one I am not sure of the exact reason, however I am sending you the attached project again with all of the necessary dlls and xaml files so you can test it on your end.

As for modifying ControlTemplates, please note that even though it might not be the easiest solution in terms of maintenance, it is sometimes necessary in order to achieve what you are going for. When upgrading to a new version, if you have modified the ControlTemplate of a control and there are changes in the ControlTemplate on our side, you will simply continue using the modified template on your side. That said I want to assure you that we don't introduce changes into the ControlTemplates of controls without a good reason.

Currently, we do not have information about changes in the visual structure of the controls in our documentation, however you can always open a new support ticket and ask if any changes have been made for a particular control/theme for a specific version and we will check it out for you.

As a final note, I will add that if you decide that the suggested approach of changing the ControlTemplate is not suitable for you, you can log a feature request in our feedback portal for introducing a mechanism to fire the cell validation on demand. If the feature request gains enough popularity, we will consider it for one of our next releases.

I hope this information helps. Let me know if I can be of any further assistance.

Regards,
Vladimir Stoyanov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Stefan
Top achievements
Rank 1
answered on 24 Sep 2018, 07:35 AM

Thank you for the explanation!
Very much appreciated! GREAT support :-)
I will have a look at the feature request page.

Regards
/Stefan

0
Vladimir Stoyanov
Telerik team
answered on 25 Sep 2018, 02:27 PM
Hello Stefan,

Thank you for the suggested feature request. We are currently in the process of reviewing it, however before proceeding with that I want to suggest two other options that might be suitable for your scenario and do not include extracting the default ControlTemplate of the GridViewRow.

First approach: 
You can use the IDataErrorInfo interface for validation and call the PropertyChanged method for the respective property that the column is bound to in the CellValidated event of the RadGridView. I am attaching a sample project where I modified the ValidationIDataErrorInfo SDK example in order to demonstrate this approach for your reference. 

Second approach:
The second approach uses the RowStyleSelector with two styles which I suggested in my previous reply, however instead of setting the Template property in one of the styles, you can set the Background property. This would give you the visual indication that the row is invalid, however the cell that is not currently edited(and has the same value as the currently edited cell) will not have its validation border shown(since the cell is not recreated). That said, I believe that such an approach is sufficient since when the current cell has a validation error, you cannot edit any other cells. Thus a simple background change of the other row that contains the same value(you can use the same Background color which is used for the Background_Invalid border) should be enough to indicate to the user that the row is invalid.

Please, review the suggested approaches and let me know if any of them will be suitable for your scenario.

Regards,
Vladimir Stoyanov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Stefan
Top achievements
Rank 1
answered on 26 Sep 2018, 02:48 PM

Hi again and THANKS for the nice ideas!

Regarding “First approach”:
Works almost perfectly. Just a small glitch, but still good enough for me. I will use this solution!
I had to do some minor modifications to your code as my model class do not implement INotifyPropertyChange (see below).

The glitch:
Run the example and change “Chelsea” to “Liverpool” and press enter. Now both cells gets the red validation-error-border (as expected). Now press “Esc” and the current cell will return to the old value (as expected). However, the error on “Liverpool” is still visible, even if clicking around in the GridView. After entering edit mode in the same column, the incorrect error will be gone.

Regarding “Second approach”:
I have not yet tested this, but I think that scrolling the mouse wheel would result in some strange behavior. When more rows are used and you scroll the non-current cell with the error out of the visible area and then scroll back, you will have the normal red validation-error-border around the non-current cell. When the current cell is changed to a unique value, I think the red background will be removed in the non-current cell as expected, but the validation-error-border would still be visible.

Just in case someone else is interested, here are some more information about the changes I made (and why):

I’m using Entity Framework (Database First) on a database with quite a lot of tables with a lot of columns. The generated model from EF do not implement INotifyPropertyChanged. In total I have almost 1000 Properties generated by EF. As there are no complex business logic involved, there is no separate model-layer (that implements INotifyPropertyChanged) and the GridView uses the data binding directly to the model generated by EF.
WPF binds to models that don’t implement INotifyProperetyChanged, by registering to the ValueChanged on the PropertyDescriptor for the property.
So I ended up with this code:

private void RadGridView_CellValidated(object sender, Telerik.Windows.Controls.GridViewCellValidatedEventArgs e)
{
    var itemsSource = this.BasdataGridView.ItemsSource as IEnumerable;
    var propertyChangedName = (e.Cell.Column as GridViewBoundColumnBase).DataMemberBinding.Path.Path;
 
    foreach (var obj in itemsSource)
    {
        var properties = TypeDescriptor.GetProperties(obj);
        PropertyDescriptor pd = properties.Find(propertyChangedName, false);
 
        //Get and Set the same value to raise ValueChanged (Will work as long as setting the same value actually will raise ValueChanged)
        //ValueChanged will then force Validation of cell
        var oldValue = pd.GetValue(obj);
        pd.SetValue(obj, oldValue);
    }
}


Regarding the feature request:
If you like the idea of my feature request, it would also be nice with a 4:th option to make it more flexible

ForceValidationOfAllVisibleCellsInRow:
Whenever a validation result of a cell changes (from no_error to error OR from error to no_error) all the visible cells in the row will be re-validated. Useful when performing complex validation using multiple columns in a row.

Once again… THANKS! :-)

Regards
/Stefan

0
Vladimir Stoyanov
Telerik team
answered on 01 Oct 2018, 12:20 PM
Hello Stefan,

I am happy to hear that my first suggestion would be suitable for your requirements. Thank you for taking the time to explain the changes that you have made in your particular scenario.

To address the problem that you are referring to, you can handle the CellEditEnded event of the RadGridView and execute the same logic that you execute in the CellValidated event. The CellEditEnded will be called when the Escape key is pressed, thus triggering the validation logic. Please, give this approach a try and let me know how it goes.

I hope you find this helpful.

Regards,
Vladimir Stoyanov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Stefan
Top achievements
Rank 1
answered on 02 Oct 2018, 07:36 AM

Hi Vladimir!

It works perfectly!
To conclude, I ended up with exactly the look and feel I was looking for :-)

Thanks for great support!

Regards
/Stefan

Tags
GridView
Asked by
Stefan
Top achievements
Rank 1
Answers by
Vladimir Stoyanov
Telerik team
Stefan
Top achievements
Rank 1
Share this question
or