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

TaskBoardCardModel.State Property Change

2 Answers 120 Views
TaskBoard
This is a migrated thread and some comments may be shown as answers.
Brian
Top achievements
Rank 1
Veteran
Brian asked on 21 Apr 2020, 11:31 AM

Hi,

I'm working on an application that uses the TaskBoard and have encountered an issue when updating the State property of the TaskBoardCardModel. When the State is updated via binding should the card not move to the appropriate column?

To reproduce the issue I've provided the XAML and view model code for your review.

<Window
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                xmlns:local="clr-namespace:TaskBoardIssue" x:Class="TaskBoardIssue.MainWindow"
                Title="MainWindow" Height="350" Width="525"
                WindowState="Maximized"
    >
    <Window.DataContext>
        <local:TaskBoardVM/>
    </Window.DataContext>
    <Grid>
 
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
 
        <StackPanel Grid.Column="1"
                    Orientation="Vertical">
 
            <Button Content="Add New Model"
                    Margin="3"
                    Command="{Binding AddModel}"
                    />
 
            <Button Content="Update State"
                    Margin="3"
                    Command="{Binding UpdateModel}"
                    />
             
            <telerik:RadPropertyGrid
                Item="{Binding TestModel}"
                />
 
        </StackPanel>
 
        <telerik:RadTaskBoard
            ItemsSource="{Binding MyTasks}"
            GroupMemberPath="State"
            AutoGenerateColumns="True"
            />
 
    </Grid>
</Window>

 

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.TaskBoard;
 
namespace TaskBoardIssue
{
    public class TaskBoardVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
 
 
        ObservableCollection<TaskBoardCardModel> myTasks;
 
        public ObservableCollection<TaskBoardCardModel> MyTasks { get; set; }
 
 
        private TaskBoardCardModel testModel;
 
        public TaskBoardCardModel TestModel
        {
            get { return testModel; }
            set
            {
                if (testModel != null)
                    if (testModel.Equals(value))
                        return;
 
                testModel = value;
                this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TestModel)));
            }
        }
 
 
        public TaskBoardVM()
        {
            TestModel = new TaskBoardCardModel() { Assignee = "Alpha", Title = "Title Text", Description = "Description Text", State = "Backlog"};
 
            MyTasks = new ObservableCollection<TaskBoardCardModel>()
            {
                new TaskBoardCardModel(){ Assignee = "Alpha", Title = "Title Text", Description = "Description Text", State = "Backlog"},
                new TaskBoardCardModel(){ Assignee = "Alpha", Title = "Title Text", Description = "Description Text", State = "Active"},
                new TaskBoardCardModel(){ Assignee = "Alpha", Title = "Title Text", Description = "Description Text", State = "Complete"},
            };
 
            AddModel = new DelegateCommand(InsertTestModel, canBeExecuted);
            UpdateModel = new DelegateCommand(UpdateTestModel, canBeExecuted);
            this.CanExecuteCommand = true;
        }
 
 
        public bool CanExecuteCommand { get; set; }
        public ICommand AddModel { get; set; }
        public ICommand UpdateModel { get; set; }
 
 
        private bool canBeExecuted(object obj)
        {
            return this.CanExecuteCommand;
        }
 
        private void InsertTestModel(object obj)
        {
            MyTasks.Add(TestModel);
            this.PropertyChanged(this, new PropertyChangedEventArgs(nameof(MyTasks)));
        }
 
        private void UpdateTestModel(object arg)
        {
            TestModel.State = "Active";
        }
 
    }
}

 

If you build and run the application, click the 'Add New Model' and you can see the model being added to the appropriate column. However when you click 'Update Model' you can see the State property does update (see Property Grid), but the Task Card does not move to the correct column.

Regards,

 

2 Answers, 1 is accepted

Sort by
0
Martin Ivanov
Telerik team
answered on 24 Apr 2020, 09:30 AM

Hello Brian,

We can confirm that this is an issue in RadTaskBoard. We logged it in our feedback portal, where you can track its status. Also, you can find your Telerik points updated.

To work this around, you can remove the corresponding TaskBoardCardModel from the ItemsSource of the control, update its State and re-add it in the ItemsSource. For example:

private void UpdateTestModel(object arg)
{
	var index = MyTasks.IndexOf(TestModel);
	MyTasks.Remove(TestModel);
	TestModel.State = "Active";
	MyTasks.Insert(index, testModel);
}

Regards,
Martin Ivanov
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
0
Brian
Top achievements
Rank 1
Veteran
answered on 24 Apr 2020, 01:19 PM

In the interim, if it helps anyone, I implemented a solution that requires a bit of code behind and a small class.

In the constructor of your view add the following code. TaskBoardStateChangeHandler is initialized in teh TaskBoard.Loaded event.

TaskBoardStateChangeHandler stateManager;
 
// View ctor
public MyTasksView()
{
    InitializeComponent();
    taskBoard.Loaded += TaskBoard_Loaded;
 
}
 
private void TaskBoard_Loaded(object sender, RoutedEventArgs e)
{
    stateManager = new TaskBoardStateChangeHandler(taskBoard);
}

 

This class hooks into the PropertyChanged event of the items in the collection. 

using System.Linq;
using System.Windows;
using Telerik.Windows.Controls;
 
public class TaskBoardStateChangeHandler
    {
        RadTaskBoard Board = null;
 
 
        public TaskBoardStateChangeHandler(RadTaskBoard board)
        {
            Board = board;
            Initialize(board);
        }
 
        ObservableCollection<TaskModel> collection;
        private void Initialize(RadTaskBoard board)
        {
            collection = board.ItemsSource as ObservableCollection<TaskModel>;
            collection.CollectionChanged += Items_CollectionChanged;
 
            // For existing items, wire up property change notification.
            foreach (var model in collection)
            {
                TaskModel cardModel = model as TaskModel;
                cardModel.PropertyChanged += Item_PropertyChanged;
            }
        }
 
 
 
        /// <summary>
        /// Use this method to hook / unhook change notification on Items.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                // Wire up the property change
                case System.Collections.Specialized.NotifyCollectionChangedAction.Add: // New Items
 
                    var newItems = e.NewItems;
 
                    foreach (TaskModel item in newItems)
                    {
                        item.PropertyChanged += Item_PropertyChanged;
                    }
 
                    break;
                // Remove the property change handler
                case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: // OldItems
 
                    foreach (TaskModel item in e.OldItems)
                    {
                        item.PropertyChanged -= Item_PropertyChanged;
                    }
 
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Replace: // OldItems
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Move: // OldItems
                    break;
                case System.Collections.Specialized.NotifyCollectionChangedAction.Reset: // Reset   4   The contents of the collection changed dramatically.
                    break;
                default:
                    break;
            }
        }
 
        // When an item property changes check to see if it is the property that is being used as the GroupMemberPath. If it is move the card to appropriate column.
        private void Item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == Board.GroupMemberPath)
            {
                MoveCard();
            }
        }
 
 
        private void MoveCard()
        {
            List<ItemTransferContainer> transfers = new List<ItemTransferContainer>();
 
            foreach (var col in Board.Columns)
            {
                if(col.Items.Count>0)
                {
                    var colState = col.GroupName as string;
 
                    var p = col.Items.First().GetType().GetProperty(Board.GroupMemberPath).GetMethod;
                    var v = p.Invoke(col.Items.First(), null);
 
                    var itemsThatDontBelong = col.Items.Where(x => (string)p.Invoke(x, null) != colState).Select(x => x).ToList();
 
 
                    foreach (var task in itemsThatDontBelong)
                    {
                        var toColumn = Board.Columns.Where(c => (string)c.GroupName == (string)p.Invoke(task, null)).FirstOrDefault();
 
                        transfers.Add(new ItemTransferContainer() { CardModel = (TaskModel)task, From = col, To = toColumn });
                    }
                }
            }
 
            Application.Current.Dispatcher.Invoke(()=> transfers.ForEach(t => t.Commit()));
 
        }
 
 
        class ItemTransferContainer
        {
            public object CardModel { get; set; }
            public TaskBoardColumn From { get; set; }
            public TaskBoardColumn To { get; set; }
 
            public void Commit()
            {
                if (!To.Items.Contains(CardModel))
                {
                    To.Items.Add(CardModel);
                }
                if (From.Items.Contains(CardModel))
                {
                    From.Items.Remove(CardModel);
                }
 
 
            }
        }
 
    }

 

Pay particular attention to this line if the item properties are being updated from a different thread. Updating the control requires the action to be performed on the UI thread.

Application.Current.Dispatcher.Invoke(()=> transfers.ForEach(t => t.Commit()));
Tags
TaskBoard
Asked by
Brian
Top achievements
Rank 1
Veteran
Answers by
Martin Ivanov
Telerik team
Brian
Top achievements
Rank 1
Veteran
Share this question
or