Previously I demonstrated how to implement the MVVM pattern in C++/CX, through a small sample app that allows the user to view the photos in their Pictures library. Since then I’ve enhanced the app by using the Repository pattern, adding design-time data, and by passing serializable objects when navigating between pages. I also wrote about how to access pixel data in a C++/CX Windows Store app.
In the MVVM blog post I mentioned that you can use data binding for UI controls that cause the app to perform operations, by binding the control’s Command property to an ICommand property on a view model. This applies to any controls that derive from ButtonBase. When the control’s command is invoked, the code in the view model is executed. The advantage of this approach is that it allows you to reduce (and in some cases, completely eliminate) the amount of code in the code-behind file for the view, and it helps to promote the testability of your app.
In this blog post I’ll further extend the app so that the user can convert a photo to greyscale, on the PhotoView page. In order to do this I’ll bind the Command property of a Checkbox control to an ICommand property on the PhotoViewModel.
Implementation
I’ve added a Checkbox control to the PhotoView page, to control whether the photo should be displayed in colour or greyscale. The Checkbox binds the IsChecked property to the IsGreyscale property on the PhotoViewModel class, to determine whether the Checkbox is checked or not. For details of how the view binds to properties on the view model see MVVM in C++/CX, and the Hilo project.
<CheckBox Command="{Binding GreyscaleCommand}"
IsChecked="{Binding Path=IsGreyscale, Mode=TwoWay}" />
ICommand^ PhotoViewModel::GreyscaleCommand::get()
{
return m_greyscaleCommand;
}
m_greyscaleCommand = ref new DelegateCommand(ref new ExecuteDelegate(this, &PhotoViewModel::ConvertPhotoToGreyscale), nullptr);
The ExecuteDelegate specifies that the ConvertPhotoToGreyscale method will be invoked when the command is executed by the user toggling the state of the Checkbox. This method uses the WriteableBitmap type to manipulate the pixel values in order to convert them to their greyscale representation. For more information see Accessing Image Pixel Data in a C++/CX Windows Store App.
void PhotoViewModel::ConvertPhotoToGreyscale(Object^ parameter)
{
if (!m_isGreyscale)
{
m_photo = nullptr;
OnPropertyChanged("Photo");
return;
}
unsigned int width = m_photo->PixelWidth;
unsigned int height = m_photo->PixelHeight;
// Create destination bitmap
WriteableBitmap^ destImage = ref new WriteableBitmap(width, height);
// Get pointers to the source and destination pixel data
byte* pSrcPixels = GetPointerToPixelData(m_photo->PixelBuffer);
byte* pDestPixels = GetPointerToPixelData(destImage->PixelBuffer);
// Convert pixel data to greyscale
parallel_for(0u, height, [width, pSrcPixels, pDestPixels](unsigned int y)
{
for (unsigned int x = 0; x < width; x++)
{
byte b = pSrcPixels[(x + y * width) * 4];
byte g = pSrcPixels[(x + y * width) * 4 + 1];
byte r = pSrcPixels[(x + y * width) * 4 + 2];
byte a = pSrcPixels[(x + y * width) * 4 + 3];
byte luminance = static_cast<byte>(0.299 * r + 0.587 * g + 0.114 * b);
pDestPixels[(x + y * width) * 4] = luminance; // B
pDestPixels[(x + y * width) * 4 + 1] = luminance; // G
pDestPixels[(x + y * width) * 4 + 2] = luminance; // R
pDestPixels[(x + y * width) * 4 + 3] = a; // A
}
});
// Update image on screen
m_photo = destImage;
OnPropertyChanged("Photo");
}
The method also includes functionality to reset the photo from greyscale to colour by forcing the original photo to be reloaded from the file system. Note that this approach is merely illustrative in order to include the functionality, and does not offer the user experience recommended for a Windows Store app.
Summary
Data binding can be used for UI controls that cause an app to perform operations, by binding the control’s Command property to an ICommand property on a view model. This applies to any controls that derive from ButtonBase. When the control’s command is invoked, the code in the view model is executed. The advantage of this approach is that it allows you to reduce (and in some cases, completely eliminate) the amount of code in the code-behind file for the view, and it helps to promote the testability of your app.
The sample application can be downloaded here.
No comments:
Post a Comment