FilterDescriptor With RadTreeView WinForms

3 Answers 69 Views
DataFilter Treeview
James
Top achievements
Rank 1
Iron
James asked on 12 Apr 2023, 09:23 AM | edited on 12 Apr 2023, 09:45 AM

Hi Everyone,

I use a RadTreeView control on my form. I have a client table and a project table. Both tables have a DeletionStatus boolean field/property.

This is how I set the control up:

this.tvProjectClient.DataSource = clientBindingSource; this.tvProjectClient.DisplayMember = "FullName"; this.tvProjectClient.ValueMember = "ClientId"; this.tvProjectClient.RadContextMenu = this.rcmProject; this.tvProjectClient.RelationBindings.Clear(); this.tvProjectClient.RelationBindings.Add(new RelationBinding(this.projectBindingSource, "Name", "ClientId", "ClientId", "ProjectId"));

 

Now i want to create a FilterDescriptor to filter DeletionStatus to false on the Client table to be like:

      FilterDescriptor filterDescriptor = new FilterDescriptor();

      filterDescriptor.Value = "False";
      filterDescriptor.Operator = FilterOperator.IsEqualTo;
      filterDescriptor.PropertyName = "DeletionStatus";

My question is, how to I change the property name to reference the client (or project) table for DeletionStatus? I have tried Client.DeletionStatus and ClientRow.DeletionStatus but the control does not register it.

Any ideas? I do not want to change field names if possible.

Many thanks, JB

James
Top achievements
Rank 1
Iron
commented on 12 Apr 2023, 12:07 PM

After doing some more testing it appears that the FilterDescriptor only filters one level of the RadTreeView at a time. I created 2 calculated fields in the dataset tables to be equal to the DeletionStatus - ClientDeletionStatus and ProjectDeletionStatus. When I try to use a composite filter descriptor with the two filters (one from each of the binding adaptors), I get an error:

System.ArgumentNullException: 'Value cannot be null. (Parameter 'key')'

I suspect that that is because there is no way to group the related data. Does anyone have any suggestions? I need the data bind to stick when the data sets changes - this works really well.

3 Answers, 1 is accepted

Sort by
0
Accepted
James
Top achievements
Rank 1
Iron
answered on 21 Apr 2023, 12:29 PM

Thanks Dinko - very helpful. After understanding this multi threaded issue, I have changed the logic of the thread that changes the datasets.

I have been using a method to receive sql notifications form SQL server using service broker and triggers. When those notifications come in, I now enqueue these messages onto a queue using an external thread as they happen.

Because a timer appears to process on the UI thread, I now poll those enqueued messages and push those changes to the dataset. This appears to work really well for insert/update/delete from SQL server. 

The final step was to reset bindings on the tree view once data is pushed through using some code to ensure that the hierarchy persists. 

So now my application is able to react to changes in the SQL server database from other users. Very happy with the result.

Thanks again Dinko for your responses. They were really helpful. I think we can close this one off now.

0
Dinko | Tech Support Engineer
Telerik team
answered on 14 Apr 2023, 12:07 PM

Hi James,

Thank you for the provided details.

You can try using RadTreeView Custom Filtering which is a flexible mechanism for filtering RadTreeView nodes by using custom logic. It has a higher priority than the applied FilterDescriptors. The nodes come one by one in the FilterNode() predicate in which you can determine whether to be visible or not depending on your logic. It is important to note that when a child node matches the filter criteria, it has to be marked as visible, as well as all of its parent nodes up to the top. This means that when a node is passed to the filter predicate you have to traverse its Nodes collection down to the bottom recursively and check whether at least one child node matches the filter. If such a child node exists, the parent node that is currently being passed to the filter predicate has to be marked as visible. 

I hope that this approach will work for you.

Regards,
Dinko | Tech Support Engineer
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

0
James
Top achievements
Rank 1
Iron
answered on 14 Apr 2023, 12:57 PM

Hi Dinko,

Thank you very much for your input. I have got around the issue by using LINQ to provide the filter (in my case using where DeletionStatus is false).

      this.clientBindingSource.DataSource = (from DS in this.crmDataSet.Client
                                             orderby DS.FullName
                                             where ((!DS.DeletionStatus) && (DS.RowState != DataRowState.Deleted))
                                             select DS).AsDataView();

This loads the RadTreeView with the right filter (note this has been simplified to highlight the issue I have).

I use a tree view called tvProjectClient and set this up as below:

      this.tvProjectClient.DataSource = clientBindingSource;
      this.tvProjectClient.ValueMember = "ClientID";
      this.tvProjectClient.DisplayMember = "FullName";
      this.tvProjectClient.RadContextMenu = this.rcmClient;

Now I have an issue when changing DeletionStatus in the client dataset. I change the dataset by changing DeletionStatus to true for a particular client. Then I use the following to reset bindings.

clientBindingSource.ResetBindings(false);

This may work for one or two changes but then it will not bind correctly. All of a sudden, the arrows do not work to expand/collapse as well.

Interesting enough when I add projects to the RadTreeView (see below), changing the DeletionStatus has the desired effect and appears/disappears as I change DeletionStatus multiple times.

      RelationBinding projRelation = new RelationBinding(this.ProjectBindingSource, "Name", "ClientId", "ClientId", "ProjectId");
      projRelation.RelationName = "Project";

      this.tvProjectClient.RelationBindings.Add(projRelation);


I have tried all sorts of methods but there either appears to be a bug or I am simply not setting this up correctly. As I have stated, using relation binding for project seems to work as expected. If I change the datasource to project instead of client, the behaviour persists. I would really appreciate your input on this.

I must note I am very impressed how the data binding just works in general which is wonderful. However, I have this particular issue that is stumbling me a little.

Thanks, James

James
Top achievements
Rank 1
Iron
commented on 14 Apr 2023, 02:50 PM | edited

I took off the ResetBindings. This has made it much more stable. The changes are coming through when using tvClientProject.Focus(). However, something weird happens when I change DeletionStatus. It may work in the first instance but after that the control does not respond to data changes.
James
Top achievements
Rank 1
Iron
commented on 18 Apr 2023, 01:18 PM

After much investigation, it looks like I have an issue due to a separate thread updating the data source. If I change DeletionStatus directly on the data source, it works. However when I change DeletionStatus using a separate thread it goes weird. I can update rows fine except when updating DeletionStatus - the filter column. There are some articles I am looking at. I am really disappointed as it so close to working. Anyone got any ideas on how to solve this?
Dinko | Tech Support Engineer
Telerik team
commented on 19 Apr 2023, 10:52 AM

I will start with that all UI controls are not thread-safe controls in the whole Windows Forms platform (not just Telerik controls, but all controls out there). Here is an article on MSDN, describing how to make a thread-safe Winforms UI application. This means that any control from the Telerik UI for WinForms suite is not thread-safe as well and cannot be used outside the main UI thread. 

If I have correctly understood your last case, updating DeletionStatus will not work while the rest of the columns update. Now updating the collection from a different thread may lead to a cross-thread exception and I am curious why this not is happening in your case. In general, updating an object from a different thread needs to wrap inside a BeginInvoke() method to avoid the cross-thread exception. The BegindInvoke() method needs to be called on the object which is currently trying to update the collection of the other thread. Give this a try and share how it goes.

Tags
DataFilter Treeview
Asked by
James
Top achievements
Rank 1
Iron
Answers by
James
Top achievements
Rank 1
Iron
Dinko | Tech Support Engineer
Telerik team
Share this question
or