Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Visual C++ and WinRT/Metro - Databinding Basics

4.85/5 (16 votes)
18 Oct 2011CPOL8 min read 100K   495  
An introduction to using WinRT databinding with Visual C++

Developer Preview Warning

This article and its contents are based on the first public developer preview of Windows 8 and Visual Studio 11. The code snippets and other information mentioned in the article may thus be subject to significant changes when the OS/VS hits beta/RTM.

Image 1

Image 2

Introduction

Metro/WinRT supports powerful databinding capabilities exposed through XAML (you can do everything in straight code too, but XAML is the recommended approach). The databinding support in WinRT differs from that used in WPF/Silverlight in its implementation although externally this is not so obvious when you use C# or VB. This is because the .NET interop code implements the required thunking/plumbing needed to smoothly transition from standard .NET code into WinRT databinding. This is not so for C++ as of the developer preview. C++ developers need to implement certain interfaces to get their classes to participate in databinding. They may change this in a future release. So this article should be looked at as one of primarily academic interest.

How you'd expect it to work

Here's some minimal C# code that shows how a TextBlock control's Text property is bound to a String property.

C#
partial class MainPage
{
    public MainPage()
    {
        InitializeComponent();

        this.DataContext = new Data();
    }
}

class Data
{
    public string Title
    {
        get
        {
            return "data binding!";
        }
    }
}

And here's the XAML.

XML
<Grid x:Name="LayoutRoot" Background="#FF0C0C0C">
        <TextBlock Text="{Binding Title}"></TextBlock>
</Grid>

Data is a regular C# class and Title is a standard C# property. No extra work was needed to make the class databindable. I expected a similar experience when using C++ (except for the syntactic changes). Suffice it to say that I was a little disappointed to find that it was not that straightforward. Again, I do hope that this will be made simpler in a future release. Until then, if you want to play with databinding or just get a hang of how things work, you'll need to add some extra code to your classes.

WinRT databinding

WinRT uses COM based databinding, and any databindable code  is expected to implement the ICustomProperty interface.

MC++
MIDL_INTERFACE("30DA92C0-23E8-42A0-AE7C-734A0E5D2782")
ICustomProperty : public IInspectable
{
public:
    virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Type( 
        /* [out][retval] */ __RPC__out Windows::UI::Xaml::Interop::TypeName *value) = 0;
    
    virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Name( 
        /* [out][retval] */ __RPC__deref_out_opt HSTRING *value) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE GetValue( 
        /* [in] */ __RPC__in_opt IInspectable *obj,
        /* [out][retval] */ __RPC__deref_out_opt IInspectable **value) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE SetValue( 
        /* [in] */ __RPC__in_opt IInspectable *obj,
        /* [in] */ __RPC__in_opt IInspectable *value) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE GetIndexedValue( 
        /* [in] */ __RPC__in_opt IInspectable *obj,
        /* [in] */ __RPC__in_opt IInspectable *index,
        /* [out][retval] */ __RPC__deref_out_opt IInspectable **value) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE SetIndexedValue( 
        /* [in] */ __RPC__in_opt IInspectable *obj,
        /* [in] */ __RPC__in_opt IInspectable *value,
        /* [in] */ __RPC__in_opt IInspectable *index) = 0;
    
    virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CanWrite( 
        /* [out][retval] */ __RPC__out boolean *value) = 0;
    
    virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CanRead( 
        /* [out][retval] */ __RPC__out boolean *value) = 0;
    
};

Here's a simpler .NET-ish version of the interface (easier to absorb).

C#
[Guid("30DA92C0-23E8-42A0-AE7C-734A0E5D2782")]
interface ICustomProperty
{
  Type Type { get; }

  string Name { get; }

  object GetValue(object target);
 
  void SetValue(object target, object value);
 
  object GetIndexedValue(object target, object indexValue);
 
  void SetIndexedValue(object target, object value, object indexValue);
  
  bool CanRead { get; }

  bool CanWrite { get; }
}

In theory all your databindable classes need this interface. In practice, it would be quite impractical and absurd to have to do that. So a good start would be to declare a class that implements this interface.

Implementing some base and helper classes for databinding

I studied the .NET implementation and saw that they have used

MC++
PropertyInfo
(the .NET reflection class that represents a property) in their implementation. Since WinRT does not have an equivalent, I thought that's what I should first write. And I chose the same name being of a rather unimaginative nature myself.

MC++
namespace ProjectInternal
{
  delegate Object^ PropertyGetValue(Object^ target);
    
  delegate void PropertySetValue(Object^ target, Object^ value);
}

ref class PropertyInfo sealed
{
  ProjectInternal::PropertyGetValue^ _propertyGetValue;
  ProjectInternal::PropertySetValue^ _propertySetValue;

public:
    PropertyInfo(String^ name, String^ typeName, 
        ProjectInternal::PropertyGetValue^ propertyGetValue, 
        ProjectInternal::PropertySetValue^ propertySetValue)
    {
      this->Name = name;

      TypeName temp;
      temp.Name = typeName;
      temp.Kind = TypeKind::Primitive;
      this->Type =  temp;

      _propertyGetValue = propertyGetValue;
      _propertySetValue = propertySetValue;
    }

    property String^ Name;
        
    property TypeName Type;

    property bool CanRead 
    { 
      bool get()
      {
        return _propertyGetValue != nullptr;
      }
    }
        
    property bool CanWrite 
    { 
      bool get()
      {
        return _propertySetValue != nullptr;
      }
    }

    Object^ GetValue(Object^ target)
    {
      return _propertyGetValue(target);
    }

    void SetValue(Object^ target, Object^ value)
    {
      _propertySetValue(target, value);
    }

};

The class basically wraps a property, and allows a caller to get or set values. And to do that it uses delegates (declared above). For the sake of simplicity (and also because I was lazy), I do not consider indexed properties (not commonly used in XAML anyway). Since we now have a

MC++
PropertyInfo
class, implementing the ICustomProperty class is easy.

MC++
ref class CustomProperty sealed : public ICustomProperty
{
  PropertyInfo^ _propertyInfo;

public:
  CustomProperty(PropertyInfo^ propertyInfo)
  {
    _propertyInfo = propertyInfo;
  }

  virtual property TypeName Type 
  { 
    virtual TypeName get()
    {
      return _propertyInfo->Type;
    }
  }

  virtual property String^ Name 
  { 
    virtual String^ get()
    {
      return _propertyInfo->Name;
    }
  }

  virtual property bool CanRead 
  { 
    virtual bool get()
    {
      return _propertyInfo->CanRead;
    }
  }

  virtual property bool CanWrite 
  { 
    virtual bool get()
    {
      return _propertyInfo->CanWrite;
    }
  }

  virtual Object^ GetValue(Object^ target)
  {
    return _propertyInfo->GetValue(target);
  }

  virtual void SetValue(Object^ target, Object^ value)
  {
    _propertyInfo->SetValue(target, value);
  }

  virtual void SetIndexedValue(Object^ target, Object^ value, Object^ index)
  {
    throw ref new NotImplementedException();
  }

  virtual Object^ GetIndexedValue(Object^ target, Object^ value)
  {
    throw ref new NotImplementedException();
  }
};

Again note how indexed properties are not supported. Now that we have an implementation for ICustomProperty we also need an implementation for ICustomPropertyProvider. In fact the simplest way you can make an object databindable is to have it implement this interface.

MC++
MIDL_INTERFACE("7C925755-3E48-42B4-8677-76372267033F")
ICustomPropertyProvider : public IInspectable
{
public:
    virtual HRESULT STDMETHODCALLTYPE GetCustomProperty( 
        /* [in] */ __RPC__in HSTRING name,
        /* [out][retval] */ __RPC__deref_out_opt Windows::UI::Xaml::Data::ICustomProperty **property) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE GetIndexedProperty( 
        /* [in] */ __RPC__in HSTRING name,
        /* [in] */ Windows::UI::Xaml::Interop::TypeName type,
        /* [out][retval] */ __RPC__deref_out_opt Windows::UI::Xaml::Data::ICustomProperty **property) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE GetStringRepresentation( 
        /* [out][retval] */ __RPC__deref_out_opt HSTRING *stringRepresentation) = 0;
    
    virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Type( 
        /* [out][retval] */ __RPC__out Windows::UI::Xaml::Interop::TypeName *value) = 0;
    
};

Minimally, you need to implement GetCustomProperty and the

MC++
Type
property. You can leave the other methods blank (or at least you can for anything demonstrated in this article).

MC++
ref class CustomPropertyProviderBase abstract 
      : public ICustomPropertyProvider, 
        public INotifyPropertyChanged, 
        public IDisposable
{
  std::map<String^, ICustomProperty^> customPropertyMap;
  bool customPropertiesAdded;

public:
  virtual ICustomProperty^ GetCustomProperty(String^ name)
  {
    if(!customPropertiesAdded)
    {
      AddCustomProperties();
      customPropertiesAdded = true;
    }

    auto it = customPropertyMap.find(name);
    return it == customPropertyMap.end() ? nullptr : it->second;
  }

  virtual ICustomProperty^ GetIndexedProperty(String^ name, TypeName typeName)
  {
    throw ref new NotImplementedException();
  }

  virtual String^ GetStringRepresentation()
  {
    throw ref new NotImplementedException();
  }

  virtual property TypeName Type 
  { 
    TypeName get()
    {
      TypeName temp;
      temp.Name = "CustomPropertyProviderBase";
      temp.Kind = TypeKind::Primitive;
      return temp;
    }
  }

  void AddCustomProperty(String^ name, String^ typeName, 
    ProjectInternal::PropertyGetValue^ propertyGetValue, 
    ProjectInternal::PropertySetValue^ propertySetValue)
  {
    customPropertyMap[name] = ref new CustomProperty
      ref new PropertyInfo(name, typeName, propertyGetValue, propertySetValue));
  }

  virtual void AddCustomProperties() = 0;

  event PropertyChangedEventHandler^ PropertyChanged;

protected:
  void FirePropertyChanged(String^ name)
  {
    if(PropertyChanged != nullptr)
    {
      PropertyChanged(this, ref new PropertyChangedEventArgs(name));
    }
  }
};

I decided to make this my base class for all my WinRT business objects (that would participate in databinding). Hence I have also implemented

MC++
IDisposable 
(compiler generated) and INotifyPropertyChanged. Please note that INotifyPropertyChanged is a COM version of the interface of the same name used by Silverlight, WPF, and Windows Forms. It serves the same purpose too - which is to notify listeners of changes in properties.

The GetCustomProperty method returns an appropriate

MC++
ICustomProperty
object for a specific property. The properties are not added here (this is a base class after all), but are instead added by derived classes. To make it easier for derived classes, there's an
MC++
AddCustomProperty
method provided that they can use. There is a virtual AddCustomProperties method that derived classes must implement where they can add any data-bound properties.

One rather annoying thing here is that you cannot directly pass a property's getter or setter to create the delegate. Instead you have to wrap a method for the getter and another for the setter and then use that for the delegate. This would be quite ridiculous for any reasonably serious business object. To avoid doing that I wrote two macros that use lambdas to generate the required delegates. Of all the things I did here this made me smile, since I was mixing macros (C/C++ feature for decades) with lambdas (modern C++ feature) to improve simplicity.

MC++
#define MAKEPROPGET(class, name) ref new ProjectInternal::PropertyGetValue(
  [](Object^ instance) {return ((class^)instance)->name;})

#define MAKEPROPSET(class, name, type) ref new ProjectInternal::PropertySetValue(
  [](Object^ instance, Object^ value) {((class^)instance)->name = (type)value;})

Databinding works now!

Here's a very simple C++ business object bound to a Xaml view.

MC++
ref class MainViewModel sealed : public CustomPropertyProviderBase
{
  String^ title;
  
  . . . 
  
  property String^ Title
  {
    String^ get()
    {
      return title;
    }

    void set(String^ value)
    {
      if(title != value)
      {
        title = value;
        FirePropertyChanged("Title");
      }
    }
  }

  virtual void AddCustomProperties() override
  {   
    AddCustomProperty("Title", "String", 
        MAKEPROPGET(MainViewModel, Title), 
        MAKEPROPSET(MainViewModel, Title, String^));
  }

And here's the XAML.

XML
<TextBlock x:Name="title" FontSize="24" TextAlignment="Left" Text="{Binding Title}" />

Did that work or what? Who's your daddy now, WinRT? :-)

The example application

I have used the MVVM pattern (loosely speaking) for the demo project. The model consists of a C++ class that's exposed as a std::list, the view-model is a WinRT wrapper (that supports data-binding), and the view is all XAML. The model data is not persisted on disk in the demo. In a real world scenario, you may fetch data from a legacy C++ library, SQL server, a JSON web-service or even a plain-text data file. I'll run through some of the code snippets to show some typical C++ to WinRT type conversions and to also give a quick overview of how the C++ data is bound to the XAML view via a WinRT middle layer. Here's what the C++ model class (a struct rather) looks like.

MC++
extern int nextId;

struct Bookmark
{
  int id;

public:
  std::wstring Author;
  std::wstring Title;
  std::wstring Url;

  Bookmark(std::wstring author, std::wstring title, std::wstring url)
      : Author(author), Title(title), Url(url)
  {
    id = nextId++;
  }

  int GetId()
  {
    return id;
  }
};

Note how the data-properties are of type std::wstring (a type not recognized by WinRT/COM). And here's a manager-class that is used by the view-model.

MC++
class BookmarkModel
{
  std::list<Bookmark> bookmarks;

public:
  BookmarkModel();

  Bookmark AddNew(std::wstring author, std::wstring title, std::wstring url)
  {
    Bookmark bookmark(author, title, url);
    bookmarks.push_back(bookmark);
    return bookmark;
  }

  void Delete(int id)
  {
    bookmarks.remove_if([=] (const Bookmark& bookmark) { return bookmark.id == id; });
  }

  void Update(int id, std::wstring author, std::wstring title, std::wstring url)
  {
    auto it = std::find_if(bookmarks.begin(), bookmarks.end(), 
      [=] (const Bookmark& bookmark){ return bookmark.id == id; });
      
    if(it != bookmarks.end())
    {
      it->Author = author;
      it->Title = title;
      it->Url = url;
    }
  }

  std::list<Bookmark> GetBookmarks()
  {
    return bookmarks;
  }
};

The manager class has all the CRUD operations. Notice the use of lambdas there, making the code pretty similar to what equivalent C# would have looked like. The model classes here are completely WinRT independent. I'd assume that that's how it would be for the vast majority of external data sources.

The view is a templated Metro ListBox.

XML
<ListBox Visibility="{Binding ShowListBox}" ItemsSource="{Binding Bookmarks}" 
         Background="Transparent" Width="440" HorizontalAlignment="Left">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Vertical" Margin="10" Width="400">
                <StackPanel.Background>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="Black"/>
                        <GradientStop Color="#FF8CA5D8" Offset="0.811"/>
                    </LinearGradientBrush>
                </StackPanel.Background>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="Author:" FontSize="16" 
                        Foreground="AntiqueWhite" Margin="0,0,2,0" />
                    <TextBlock Text="{Binding Author}" Foreground="AntiqueWhite" FontSize="16" />
                </StackPanel>
                <TextBlock Text="{Binding Title}" FontSize="16" />
                <TextBlock x:Name="url" Tag="{Binding}" Text="{Binding Url}" FontSize="16" />
                <StackPanel Orientation="Horizontal" 
                  DataContext="{Binding ElementName=LayoutRoot,Path=DataContext}">
                    <Button Command="{Binding OpenCommand}" 
                      CommandParameter="{Binding ElementName=url,Path=Text}" Content="Open" />
                    <Button Command="{Binding DeleteCommand}" 
                      CommandParameter="{Binding ElementName=url,Path=Tag}">Delete</Button>
                    <Button Command="{Binding EditCommand}" 
                      CommandParameter="{Binding ElementName=url,Path=Tag}">Edit</Button>
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Notice all the data-bindings that are defined in the XAML. If you've never used XAML before, it's going to take you a while to get used to defining UI in this manner. It's a lot easier if you've done any WPF or Silverlight. The editor panel is very similar.

XML
<StackPanel Visibility="{Binding ShowEditor}" x:Name="editor" Width="700"
            HorizontalAlignment="Left"
            Orientation="Vertical" Margin="10" Background="DarkGreen">
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock FontSize="16" Width="70" >Author:</TextBlock>
        <TextBox Text="{Binding EditAuthor, Mode=TwoWay}" Width="500" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock FontSize="16" Width="70">Title:</TextBlock>
        <TextBox Text="{Binding EditTitle, Mode=TwoWay}" Width="500" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock FontSize="16" Width="70">Url:</TextBlock>
        <TextBox Text="{Binding EditUrl, Mode=TwoWay}" Width="500" />
    </StackPanel>
    <Button Command="{Binding SaveCommand}">Save</Button>
</StackPanel>

One thing to note here is how the bindings are two-way here. This is because I also want the UI changes to propagate back to the business objects. You probably must have noticed all the command bindings in there. To facilitate this, I used a very simple ICommand implementation. Anyone who's used any form of MVVM before would recognize it as a standard command class with execute and can-execute methods.

MC++
delegate void ExecuteDelegate(Object^ parameter);
delegate bool CanExecuteDelegate(Object^ parameter);

ref class DelegateCommand sealed : public ICommand
{
  ExecuteDelegate^ _executeDelegate;
  CanExecuteDelegate^ _canExecuteDelegate;

public:
  DelegateCommand(ExecuteDelegate^ executeDelegate, 
        CanExecuteDelegate^ canExecuteDelegate = nullptr)
  {
    _executeDelegate = executeDelegate;
    _canExecuteDelegate = canExecuteDelegate;
  }

  event EventHandler^ CanExecuteChanged;

  void Execute(Object^ parameter)
  {
    _executeDelegate(parameter);
  }

  bool CanExecute(Object^ parameter)
  {
    return _canExecuteDelegate == nullptr || _canExecuteDelegate(parameter);
  }
};

Hooking up the view-model

The most important thing to do (outside of defining the actual properties and commands) is to add them to property chain (which then enables data binding on those properties). I spent many a foolish moment pondering what the heck was wrong before realizing that I had forgotten to add an entry for a newly added property.

MC++
virtual void AddCustomProperties() override
{
  AddCustomProperty("Title", "String",
      MAKEPROPGET(MainViewModel, Title),
      MAKEPROPSET(MainViewModel, Title, String^));

  AddCustomProperty("UpdateTitleCommand", "ICommand",
      MAKEPROPGET(MainViewModel, UpdateTitleCommand),
      MAKEPROPSET(MainViewModel, UpdateTitleCommand, ICommand^));

  AddCustomProperty("Bookmarks", "IObservableVector<Object>",
      MAKEPROPGET(MainViewModel, Bookmarks), nullptr);

  AddCustomProperty("OpenCommand", "ICommand",
      MAKEPROPGET(MainViewModel, OpenCommand),
      MAKEPROPSET(MainViewModel, OpenCommand, ICommand^));

  AddCustomProperty("EditCommand", "ICommand",
      MAKEPROPGET(MainViewModel, EditCommand),
      MAKEPROPSET(MainViewModel, EditCommand, ICommand^));

  AddCustomProperty("DeleteCommand", "ICommand",
      MAKEPROPGET(MainViewModel, DeleteCommand),
      MAKEPROPSET(MainViewModel, DeleteCommand, ICommand^));

  AddCustomProperty("SaveCommand", "ICommand",
      MAKEPROPGET(MainViewModel, SaveCommand),
      MAKEPROPSET(MainViewModel, SaveCommand, ICommand^));

  AddCustomProperty("AddCommand", "ICommand",
      MAKEPROPGET(MainViewModel, AddCommand),
      MAKEPROPSET(MainViewModel, AddCommand, ICommand^));

  AddCustomProperty("ShowListBox", "String",
      MAKEPROPGET(MainViewModel, ShowListBox),
      MAKEPROPSET(MainViewModel, ShowListBox, String^));

  AddCustomProperty("ShowEditor", "String",
      MAKEPROPGET(MainViewModel, ShowEditor), nullptr);

  AddCustomProperty("EditAuthor", "String",
      MAKEPROPGET(MainViewModel, EditAuthor),
      MAKEPROPSET(MainViewModel, EditAuthor, String^));

  AddCustomProperty("EditTitle", "String",
      MAKEPROPGET(MainViewModel, EditTitle),
      MAKEPROPSET(MainViewModel, EditTitle, String^));

  AddCustomProperty("EditUrl", "String",
      MAKEPROPGET(MainViewModel, EditUrl),
      MAKEPROPSET(MainViewModel, EditUrl, String^));
}

That's the boring repetitive bit that I personally hope they will automate for us in a future release. A typical data property would like this.

MC++
property String^ EditAuthor
{
  String^ get()
  {
    return editAuthor;
  }

  void set(String^ value)
  {
    if(editAuthor != value)
    {
      editAuthor = value;
      FirePropertyChanged("EditAuthor");
    }
  }
}

That's regular looking code there. Except for the call to

MC++
FirePropertyChanged 
(which is needed to tell WinRT that a property was updated). The commands are similar except they need to be hooked up to the right methods.

MC++
OpenCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &MainViewModel::Open));
EditCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &MainViewModel::Edit));
DeleteCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &MainViewModel::Delete));
SaveCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &MainViewModel::Save));
AddCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &MainViewModel::Add));

The important bits in the rest of the code have mostly to do with the WinRT C++ interaction between the view-model and the model. Here's an example of how the data is read.

MC++
void LoadData()
{
  auto items = bookmarkModel.GetBookmarks();
  for(auto it = items.begin(); it != items.end(); it++)
  {
    Bookmarks->Append(ref new BookmarkViewModel(*it));
  }
}

BookmarkViewModel is the view-model databindable wrapper that represents a C++ Bookmark object.

MC++
ref class BookmarkViewModel sealed : public CustomPropertyProviderBase
{
  String^ author;
  String^ title;
  String^ url;

public:
  BookmarkViewModel(Bookmark& bookmark)
  {
    Id = bookmark.GetId();
    Author = ref new String(bookmark.Author.c_str());
    Title = ref new String(bookmark.Title.c_str());
    Url = ref new String(bookmark.Url.c_str());
  }

  property int Id;

  property String^ Author
  {
    . . .
  }

  property String^ Title
  {
    . . .
  }

  property String^ Url
  {
    . . .
  }

  virtual void AddCustomProperties() override
  {   
    AddCustomProperty("Author", "String", 
        MAKEPROPGET(BookmarkViewModel, Author), 
        MAKEPROPSET(BookmarkViewModel, Author, String^));
    
    AddCustomProperty("Title", "String", 
        MAKEPROPGET(BookmarkViewModel, Title),
        MAKEPROPSET(BookmarkViewModel, Title, String^));
    
    AddCustomProperty("Url", "String", 
        MAKEPROPGET(BookmarkViewModel, Url), 
        MAKEPROPSET(BookmarkViewModel, Url, String^));
  }
};

The constructor converts types as needed and we also hook up the custom properties that will participate in data-binding. Here's how the delete command is implemented.

MC++
void Delete(Object^ parameter)
{
  BookmarkViewModel^ bookmarkViewModel = (BookmarkViewModel^)parameter;
  unsigned int index;
  if(Bookmarks->IndexOf(bookmarkViewModel, index))
  {
    try
    {
      Bookmarks->RemoveAt(index);
      bookmarkModel.Delete(bookmarkViewModel->Id);      
    }
    catch(...)
    {
    }
  }
}

Ignore the try-catch there (that's to work around a bug in the dev preview). Note how separate deletion calls are made into the WinRT v-m and to the C++ model.

Edit and Add work similarly. They both bring up the editor panel and delegate the actual save-task to the Save method.

MC++
void Edit(Object^ parameter)
{
  BookmarkViewModel^ bookmarkViewModel = (BookmarkViewModel^)parameter;
  Bookmarks->IndexOf(bookmarkViewModel, editIndex);
  editId = bookmarkViewModel->Id;
  this->EditAuthor = bookmarkViewModel->Author;
  this->EditTitle = bookmarkViewModel->Title;
  this->EditUrl = bookmarkViewModel->Url;   

  this->ShowListBox = "Collapsed";
}

void Add(Object^ parameter)
{
  editId = -1;
  this->EditAuthor = "";
  this->EditTitle = "";
  this->EditUrl = "";   

  this->ShowListBox = "Collapsed";
}

void Save(Object^ parameter)
{
  if(editId == -1)
  {
    auto bookmark = bookmarkModel.AddNew(
      this->EditAuthor->Data(), this->EditTitle->Data(), this->EditUrl->Data());
    Bookmarks->Append(ref new BookmarkViewModel(bookmark));
  }
  else
  {
    BookmarkViewModel^ bookmarkViewModel = (BookmarkViewModel^)Bookmarks->GetAt(editIndex);
    bookmarkViewModel->Author = this->EditAuthor;
    bookmarkViewModel->Title = this->EditTitle;
    bookmarkViewModel->Url = this->EditUrl;       

    bookmarkModel.Update(
      editId, bookmarkViewModel->Author->Data(), 
      bookmarkViewModel->Title->Data(), bookmarkViewModel->Url->Data());
  }

  editId = -1;

  this->ShowListBox = "Visible";
} 

There's nothing of great interest there except for the data-conversions. You'll end up doing this all the time when you work in WinRT since you have to convert the C++ types to the COM types (and in reverse). This is automated for you by .NET when you work in C# or VB.

Note how I used a String property to represent Visibility (which is an enum). This is to work around a bug in WinRT in the dev preview where enums do not participate in data-binding. The bug is known and has been reported.

Conclusion

Working on really early releases like the dev-preview can be frustrating but it can also provide for some very interesting moments. It also gives you opportunities to look under the covers a bit and discover how things actually work behind the scenes. This can often help you make good design decisions or choose appropriate coding patterns. Feel free to provide feedback and criticism, or ask questions through the forum at the bottom of this article.

History

  • Article first published : October 18, 2011

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)