As the best way to learn something is to play with it, so I build a little mvvm data system. Actually I’ve got similar with winforms (MVP with passive views), so I try to add another UI to the system.

A common data from contains three components:

  • filter
  • grid view and
  • detail view (of one record)

Because grid view is the same for all data objects (with the difference of the columns), I decided to keep it as a one user control and only configure columns.

And so is my solution:

GridController.cs:

    [NotifyPropertyChanged]
    public abstract class GridController : ViewModelBase, IGridController, IGridConfiguator
    {
        private IDictionary<string, object> m_Criteria;

        protected Type m_CurrentType;

        private IPager m_Pager;

        public bool AllowMultiselect { get; set; }

        public object CurrentItem { get; set; }

        public IMultidataRepository DataRepository { get; set; }

        /// <summary>
        /// Gets or sets the grid data. Data can be aded by that property, or by ShowData function
        /// </summary>
        /// <value>The grid data.</value>
        public IList GridData { get; protected set; }

        public IPager Pager
        {
            get
            {
                return m_Pager;
            }
            set
            {
                m_Pager = value;
                m_Pager.PagesChanged += Pager_PagesChanged;
            }
        }

        public event EventHandler GoToFilter;

        public event EventHandler ShowDetail;

        /// <summary>
        /// Loads the data. If m_Criteria are null or empty then it should equal as GetAll()
        /// </summary>
        public void LoadData()
        {
            GridData.Clear();
            using (new Workspace())
            {
                var x = DataRepository.FilterByProperties(m_CurrentType, m_Criteria, null, Pager.RecordFrom, Pager.PageSize);
                foreach (var item in x)
                {
                    GridData.Add(item);
                }
            }
        }

        public void OnGoToFilter()
        {
            if (GoToFilter != null)
            {
                GoToFilter(this, EventArgs.Empty);
            }
        }

        public void OnShowDetail()
        {
            if (ShowDetail != null)
            {
                ShowDetail(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Shows the data. Data will be loaded form the storage and displayed in a grid
        /// </summary>
        ///
<param name="criteria">The criteria.</param>
        public void ShowData(IDictionary<string, object> criteria)
        {
            m_Criteria = criteria;
            LoadData();
        }

        private void Pager_PagesChanged(object sender, EventArgs e)
        {
            LoadData();
        }

        public virtual void Configure(DataGrid gridToConfigure)
        {
        }
    }

It is a ModeView class which contains IPager class for switching between pages of records.
NotifyPropertyChanged class attribute is for Postsharp to add required code for properties.
And the GenericView:

    <StackPanel>
        <local:GridToolbar></local:GridToolbar>
        <wt:DataGrid x:Name="GridWithData" ItemsSource="{Binding GridData, Mode=OneWay}" SelectedItem="{Binding CurrentItem}">
        </wt:DataGrid>
    </StackPanel>

Generic grid view contains grid toolbar and a grid wich is common for every data object. And additionally it requires code behind file:

    public partial class GenericGridView : UserControl
    {
        public GenericGridView()
        {
            InitializeComponent();

            Loaded += GenericGridView_Loaded;
        }

        void GenericGridView_Loaded(object sender, RoutedEventArgs e)
        {
            ConfigureGrid();
        }

        private void ConfigureGrid()
        {
            IGridConfiguator c = DataContext as IGridConfiguator;
            if (c != null)
            {
                GridWithData.AutoGenerateColumns = false;
                c.Configure(GridWithData);
            }
            else
            {
                GridWithData.AutoGenerateColumns = true;
            }
        }
    }

Of course Configure function should be implemented in concrete class eg. PersonGridViewModel:

    [NotifyPropertyChanged]
    public class PersonGridViewModel : GridController
    {
        public PersonGridViewModel()
        {
            m_CurrentType = typeof(PersonForGrid);
            GridData = new ObservableCollection<PersonForGrid>();
        }

        public override void Configure(DataGrid gridToConfigure)
        {
            gridToConfigure.Columns.Add(
                new DataGridTextColumn()
                {
                    Header = "First name",
                    IsReadOnly = true,
                    Binding = new Binding(PersonForGrid.FIRSTNAME)
                });
            gridToConfigure.Columns.Add(
                new DataGridTextColumn()
                {
                    Header = "Surename",
                    IsReadOnly = true,
                    Binding = new Binding(PersonForGrid.LASTNAME)
                });
            gridToConfigure.Columns.Add(
                new DataGridTextColumn()
                {
                    Header = "Birth date",
                    IsReadOnly = true,
                    Binding = new Binding(PersonForGrid.BIRTHDATY)
                    {
                        StringFormat = "d"
                    }
                });
        }
    }

Actually that solution fits me, but probably it not the last word :-)

Post a Comment

*
*