Specifying Data Source Using MVVM in WPF App?

Nov 18, 2014 at 7:19 PM
The documentation states that the data source can be specified in one of three ways:
  • Using TimelineTray.Urls element from XAML
  • Using TimelineTray.ResetEvent method passing xml to it
  • Using TimelineTray.ResetEvent method passing list of TimelineEvent objects
However, calling a method like ResetEvent() breaks the MVVM pattern that I am trying to set up in my WPF app.

I have a view model that exposes, as a property, a collection of TimelineEvent objects. My view model implements the INotifyPropertyChanged interface, so I can notify the view that the TimelineEvent collection has changed. How can I get this change back to the timeline control without calling the ResetEvent() method? Is there a property that I can bind to on the control that can get a copy of the new collection?
Nov 18, 2014 at 8:30 PM
This works, but it's not very MVVM-friendly ... any suggestions?
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.ContentRendered += MainWindow_ContentRendered;
        }

        /// <summary>
        /// Notifies the control of the new events.
        /// </summary>
        /// <remarks>
        /// This is a hack required by the Timeline control. The Timeline control receives notifications
        /// of new events through a method called ResetEvent() on the TimelineTray object. This breaks
        /// the MVVM pattern, as the view model should be communicating with the view simply through data
        /// binding. The question of how to do this in an MVVM-friendly manner is in front of the developer
        /// of the timeline control project; in the interim, this hack is in place. It has been pulled
        /// off into its own method so that it can be replaced when a better way is found.
        /// </remarks>
        void MainWindow_ContentRendered(object sender, System.EventArgs e)
        {
            var MainWin = Application.Current.MainWindow as EventStudioWpf.Views.MainWindow;
            var TimelineControl = FindChild<TimelineLibrary.TimelineTray>(MainWin, "timeline");
            var MainViewModel = MainWin.DataContext as EventStudioWpf.ViewModels.MainViewModel;
            TimelineControl.ResetEvents(MainViewModel.Events);
        }

        /// <summary>
        /// Finds a Child of a given item in the visual tree. 
        /// </summary>
        /// <param name="parent">A direct parent of the queried item.</param>
        /// <typeparam name="T">The type of the queried item.</typeparam>
        /// <param name="childName">x:Name or Name of child. </param>
        /// <returns>The first parent item that matches the submitted type parameter. 
        /// If not matching item can be found, 
        /// a null parent is being returned.</returns>
        public static T FindChild<T>(DependencyObject parent, string childName)
           where T : DependencyObject
        {
            // Confirm parent and childName are valid. 
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = System.Windows.Media.VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = System.Windows.Media.VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child. 
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;
                    // If the child's name is set for search
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        // if the child's name is of the request name
                        foundChild = (T)child;
                        break;
                    }
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }

            return foundChild;
        }
    }
Coordinator
Nov 21, 2014 at 9:24 PM
MVVM is not supported. There could be some simple cases, say to call reset from collection property setter in your model, but to implement MVVM fully, all the way down to TimelineEvent object, watching new/deleted/updated collection items, property changes, etc., that appeared too time consuming.