Skip to content

Update to WinRT Database Project

I mentioned months before my WinRT database project.  I was going to stop working on it, but unfortunately as of now there is no good alternative to structured storage in WinRT applications.  SQLite team is working on WinRT version, but there is no projected date as far as I know.

So, upon some thinking I decided to maintain the project.  I took time to update the code to Windows 8 Consumer Preview and VS 11 beta.  I added unit test project with basic tests and a sample project called WinRTDbQuickStart to the solution on CodePlex.  If you download source code, you can get a quick tutorial by looking at the sample project.

I added a utility class to help with data binding.  It can serve as base class for your object that you store in database.  Here is full code for the helper class:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WinRTDatabase
{
    /// <summary>
    /// Base class for objects that can be put inside tables
    /// </summary>
    public class PropertyChangedCore : INotifyPropertyChanged
    {
        /// <summary>
        /// Set property value
        /// </summary>
        /// <typeparam name="T">Type of property</typeparam>
        /// <param name="value">New value</param>
        /// <param name="backingField">Backing field for property</param>
        /// <param name="propertyName">Name of property, leave blank</param>
        protected void SetProperty<T>(T value, ref T backingField, [CallerMemberName] string propertyName = "")
        {
            if (backingField == null && value != null)
            {
                backingField = value;
                OnPropertyChanged(propertyName);
            }
            else if (backingField != null && value == null)
            {
                backingField = value;
                OnPropertyChanged(propertyName);
            }
            else if (backingField != null && value != null && !backingField.Equals(value))
            {
                backingField = value;
                OnPropertyChanged(propertyName);
            }
        }

        /// <summary>
        /// Property changed event
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises <see cref="PropertyChanged"/> event/>
        /// </summary>
        /// <param name="propertyName">Name of property to raise event for</param>
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

The code is similar to template that is used in Metro style applications project templates distributed with VS 11.  However, I made an enhancement to it, comparing old value with new value for each property, and only raising property changed event if new value is different.  I had to do that because I ran into an issue with data binding, when property setter was invoked by data binding, event though nothing was changed.  Maybe it is a but in WinRT?

There is also facility that maintains state in database, so that you always check IsDirty flag on database to find out if you need to save.  I demonstrate the use of that property in my sample project by disabling Save button.

Most interesting code is in ViewModel class.  Here is the code for it.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WinRTDatabase;

namespace WinRTDbQuickStart
{
    public class MainViewModel : PropertyChangedCore
    {
        public string DatabaseName = "TestDb";
        private Database _database;

        public MainViewModel()
        {
            DeleteCommand = new SimpleCommand<Person>(OnDelete);
            AddCommand = new SimpleCommand<Person>(OnAdd, CanAdd);
            SaveCommand = new SimpleCommand<object>(OnSave, CanSave);
            Initialise();
        }

        public async void Initialise()
        {
            //await Database.DeleteDatabase(DatabaseName, StorageLocation.Local);
            var exists = await Database.DoesDatabaseExistsAsync(DatabaseName, StorageLocation.Local);
            if (!exists)
            {
                _database = await Database.CreateDatabaseAsync(DatabaseName, StorageLocation.Local);
                _database.CreateTable<Person>();
                var table = await _database.Table<Person>();
                await _database.SaveAsync();
                People = table;
            }
            else
            {
                _database = await Database.OpenDatabaseAsync(DatabaseName, true, StorageLocation.Local);
                People = await _database.Table<Person>();
            }

            SaveCommand.RaiseCanExecuteChanged();
            AddCommand.RaiseCanExecuteChanged();
            if (People.Count > 0)
            {
                SelectedPerson = People.First();
            }
            _database.PropertyChanged += new PropertyChangedEventHandler(_database_PropertyChanged);
        }

        void _database_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            SaveCommand.RaiseCanExecuteChanged();
            AddCommand.RaiseCanExecuteChanged();
        }

        private Table<Person> people;

        public Table<Person> People
        {
            get { return people; }
            set
            {
                people = value; OnPropertyChanged("People");
                if (people.Count > 0)
                {
                    SelectedPerson = people.First<Person>();
                }
            }
        }

        private Person selectedPerson;

        public Person SelectedPerson
        {
            get { return selectedPerson; }
            set
            {
                selectedPerson = value;
                OnPropertyChanged("SelectedPerson");
            }
        }

        public SimpleCommand<Person> DeleteCommand { get; set; }

        public void OnDelete(Person parameter)
        {
            if (parameter != null)
            {
                People.Remove(parameter);
                if (People.Count > 0)
                {
                    SelectedPerson = People.First();
                }
            }
        }

        public SimpleCommand<Person> AddCommand { get; set; }

        public void OnAdd(Person parameter)
        {
            People.Add(new Person { PersonID = Guid.NewGuid(), FirstName=" ", LastName = " " });
            SelectedPerson = People.Last();
        }
        public bool CanAdd(object parameter)
        {
            return (_database != null && People != null);
        }

        public SimpleCommand<object> SaveCommand { get; set; }

        public void OnSave(object parameter)
        {
            if (_database != null)
            {
                _database.SaveAsync();
            }
        }
        public bool CanSave(object parameter)
        {
            return (_database != null && !_database.IsBusy && _database.IsDirty);
        }
    }
}

 

Code is pretty simple, although I wrote a simple command to help me along that implements ICommand.

using System;
using System.Windows.Input;

namespace WinRTDbQuickStart
{
    public class SimpleCommand<T> : ICommand
    {
        private Action<T> _executeMethod;
        private Func<T, bool> _canExecuteMethod;

        public SimpleCommand(Action<T> executemethod, Func<T, bool> canExecuteMethod)
        {
            _executeMethod = executemethod;
            _canExecuteMethod = canExecuteMethod;
        }

        public SimpleCommand(Action<T> executemethod)
        {
            _executeMethod = executemethod;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecuteMethod == null)
            {
                return true;
            }
            else
            {
                return _canExecuteMethod((T)parameter);
            }
        }

        public event EventHandler CanExecuteChanged;
        protected void OnCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
            {
                CanExecuteChanged(this, EventArgs.Empty);
            }
        }

        public void Execute(object parameter)
        {
            _executeMethod((T)parameter);
        }

        public void RaiseCanExecuteChanged()
        {
            OnCanExecuteChanged();
        }

    }
}

As you can see, I am binding ListBox control directly to the data stored in a table.

Another important thing to notice.  All store and retrieve operations as async.  You need to make sure you are following this pattern and are using await keyword as needed.

Please let me know if you have any questions or suggestions.

Thanks.

Post to Twitter

Code Mastery event in Boston

As you may know, Magenic has been putting on Code Mastery events for quite some time.  This is a free event, similar to Code Camp, but is put on by our company,   We do it every year in all the cities where Magenic has an office.  Code Mastery events are designed to present attendees with meaningful technical content by the professionals that are using them every day, in the trenches.

Our Boston event is coming up soon.  Check out the details on Code Mastery site

http://codemastery.com/boston/

Post to Twitter

More on WinDbg, Memory Leaks and Silverlight

I blogged a long time ago on how to use WinDbg to find memory leaks in Silverlight application.  Yesterday I had to face a variation of the problem, but this time I had to debug a custom issue.  So, what we did is have customer create memory dumps.  To do that we instructed them to run the application in IE, what periodically for memory used by IE, then when memory got unreasonably high, take memory dump.  You can do that from Task Manager.  Just right-click on your process (iexplore.exe), and select option Create Dump File.  Once that is done, just have your customer transfer the file to you.

Once you have it on your local hard drive, start WinDbg (32 bit version) and under File menu select Open Crash Dump and point to your file.  Once thing you must have is the same version of Silverlight as they use.  If you do not, find a machine that has that version and copy the content of C:\Program Files (x86)\Microsoft Silverlight\{target version} onto your machine into the same folder.  You may have to use C:\Program Files\Microsoft Silverlight if the customer is running 32 bit OS because the dump file will refer to that path.

Once that is done, in command line window (bottom of WinDbg screen) type

.load C:\Program Files (x86)\Microsoft Silverlight\4.1.10111.0\sos.dll

where the path you use is the one containing the same version of Silverlight.

Once that is done, you need to load CLR debugging modules

.cordll -ve -u –l

You may also need to expand search path for WinDbg to find debugging symbols

.exepath+ C:\Program Files\Microsoft Silverlight\4.1.10111.0\

After that you are ready to run

!dumpheap -stat

Then continue as described in my previous post.  What have we achieved here you ask?  I just implemented remote debugging on client machine!  Pretty cool, hah?

Thanks.

Post to Twitter