WinUI Stuff: How to program Property Change Notification and Data Binding to keep your UI in sync (C++)

What is in for you

In today’s tutorial (which is actually already number four) we will have a deeper look at how the UI is being updated when a change to the program data occurs. So UI and internal

If you have followed the previous tutorials, you might have wondered where the UI is being updated. We made some changes to the internal data structures (like performing a copy & paste operation) but we never really cared about the visual appearance. Obviously, WinUI provides some “magic” which does this kind of updates in the background.

Like in the previous tutorials, we will once more use the handling of an RNA molecule with its nucleotides (if this is the first How 2 you are reading, then check the introduction of the first tutorial “How to make a Data Template Selector/Part I” about the background of the biochemistry we simulate). Today, I won’t add too much code. I will rather mainly stick to the code of tutorial number three and have a deeper look at how the UI update works internally.

But since a tutorial without any (new) code might be a bit boring, we will add a small feature to our “RNA designer” and add a control which will tell us the number of nucleotides in the RNA. That control is a perfect example of how UI updating happens in the background.

The user experience is fairly simple. We will just have an additional control showing the number of nucleotides in the RNA. The control will be updated by the app when a change to the RNA occurs.

If we copy a nucleotide sequence e.g. from the windows notepad to the clipboard…

… we can paste it using the “Paste at end” button and we will see the RNA as well as the number of nucleotides:

In this tutorial you will learn about:

  • The principles of data binding
  • How and when to use mono-directional and bi-directional data binding
  • How to use property change notifications.

You will find the code in my GitHub repositories: https://github.com/AgentSmith-Dev/How2-WinUI

What you should already know

The User Experience

We need an additional control in the main window which is MainWindow.xaml. Our control is composed of two <TextBlock> elements. One showing the title, the other one showing the number of nucleotides. For a background color and a border we wrap everything up in a <Grid> structure:

<Grid   Margin="0, 10, 0, 0"    Width="150"
        BorderBrush="{StaticResource  CardStrokeColorDefaultBrush}"
        Background="{StaticResource CardBackgroundFillColorSecondaryBrush}"
        BorderThickness="1"
        CornerRadius="{StaticResource   ControlCornerRadius}"
        HorizontalAlignment="Left">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBlock    Grid.Row="0"    
                  Text="Number of Nucleotides"    
                  Style="{StaticResource  CaptionTextBlockStyle}"
                  HorizontalAlignment="Center"
                  />
    <TextBlock    Grid.Row="1"    
                  Text="{x:Bind   hstrGetRNANbNucleotides, Mode=OneWay}"    
                  Style="{StaticResource  TitleTextBlockStyle}"
                  HorizontalAlignment="Center"
                  />
</Grid>
MainWindow.xaml

There is nothing special about the xaml statements so far. It is just one attribute where all the “magic” starts which tells WinUI which text to be displayed:

Text="{x:Bind   hstrGetRNANbNucleotides, Mode=OneWay}"
MainWindow.xaml

The code behind function hstrGetRNAnbNucleotides() is being called which obviously will return the number of the nucleotides (let’s skip the “Mode” parameter for a couple of minutes).

In the IDL we declare the function for the COM-like environment:

namespace How2_PropertyChange_DataBinding
{
    [default_interface]
    runtimeclass MainWindow : Microsoft.UI.Xaml.Window, Microsoft.UI.Xaml.Data.INotifyPropertyChanged
    {
                    MainWindow();

        clWinUIRNA  GetWinUIRNA_Window_Property{get;};
            Boolean bHasRNAClipboardData;
            Boolean bHasRNAData{get;};
            String  hstrGetRNANbNucleotides{get;};
    }
}
MainWindow.idl

The implementation uses our vector with the nucleotides, asks for its size and converts it to a string:

//  ------------------------------------------------------------------------------------------------------
/*! \brief  property (to be used by XAML): number of nucleotides in the RNA object
*   
*   \date   09/24/2025  AGS Start
*/
//  ------------------------------------------------------------------------------------------------------
hstring MainWindow::hstrGetRNANbNucleotides()
{
		return  to_hstring(this->m_WinUIRNA.Get_obsvecRNA_Property().Size());
}
MainWindow.cpp

Architecture

In the previous tutorials we already talked about the overall architecture of the application. As you see, our main window has access to the RNA data:

App.xaml wrapping MainWindow.xaml wrapping clWinUIContrl_RNAList.xaml wrapping ListView

Architecture of the WinUI3 HowTo „DataTemplateSelector – Part I“

From a MVVM (model view-view model) or MVC (model view control) perspective MainWindow.xaml represents the view and m_WinUIRNA the model. 

Data Binding

The mechanism to connect the view and the model with each other is called “Data Binding”:

Databinding in View-Model context

That’s exactly what happens in the XAML directive above, the function hstrGetRNAnbNucleotides() allows the view to connect to the model. 

Direction of the data flow with Data Binding

The previously mentioned “Mode” parameter is set to “OneWay” which means that the view is being updated by the data from the model. If we change the data in the view and keep Mode=OneWay the data in the model will not be updated. Of course not, there is no connector in the model available. If we want a two-way connection, we had to set Mode=TwoWay and we had to implement an additional “connector” on the model side. We will look at this later on.

Property change notifications

Now we have a connection between the view and the model in place. What we still need is a mechanism to inform the view (our main window, more precise our control in the main window) about a change in the model.

In WinUI this is being done by events (we already used events in tutorial number three for our copy & paste operation. There, we wanted to get informed by third party applications about a change in the clipboard). Each event which an application uses is specific for a certain type of “delegate” (a delegate is a function which is being called when the event is being raised). For the property changed notification the delegate is of type IPropertyChangedNotification. What we therefore need is an event which can be used for the communication between view and model. The event template is PropertyChangedEventHandler. Since the model needs to inform the view, the event is part of the MainWindow class:

private:

  event_token m_EventToken_ClipboardChanged;  //!<    event token for clipboard changed event
  winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler>	m_MainFrame_PropertyChanged;    //!<    event token for property changed notifications


  winrt::How2_PropertyChange_DataBinding::clWinUIRNA  m_WinUIRNA;
	                                            bool    m_bHasRNAClipboardData = false;
MainFrame.h

Furthermore, we need to add the INotifyPropertyChanged interface to the class which provides the event. That is done by inheritance in the IDL:

namespace How2_PropertyChange_DataBinding
{
    [default_interface]
    runtimeclass MainWindow : Microsoft.UI.Xaml.Window, Microsoft.UI.Xaml.Data.INotifyPropertyChanged
    {
                    MainWindow();

        clWinUIRNA  GetWinUIRNA_Window_Property{get;};
            Boolean bHasRNAClipboardData;
            Boolean bHasRNAData{get;};
            String  hstrGetRNANbNucleotides{get;};
            String  strRNANbNucleotides_Property;

    }
}
MainWindow.idl

The INotifyPropertyChanged interface requires the possibility of adding and removing delegates. Therefore, you would get an error message when you would compile the IDL by now. It would complain about two missing functions: PropertyChanged(). We need to add these as well:

  struct MainWindow : MainWindowT<MainWindow>
  {
      MainWindow();
		~MainWindow();

      winrt::How2_PropertyChange_DataBinding::clWinUIRNA  GetWinUIRNA_Window_Property();
                                  bool    bHasRNAClipboardData();
                                   void   bHasRNAClipboardData(bool value);
									 bool   bHasRNAData();

							    hstring    hstrGetRNANbNucleotides();
							    
                        winrt::event_token  PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
                                    void    PropertyChanged(winrt::event_token const& token) noexcept;
MainWindow.xaml.h
    //  ------------------------------------------------------------------------------------------------------
    /*! \brief  init/deinit handler for property changed events
    *   \date   09/15/2025  AGS Start
    */
    //  ------------------------------------------------------------------------------------------------------
    winrt::event_token MainWindow::PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
    {
        return m_MainFrame_PropertyChanged.add(handler);
    }

    void MainWindow::PropertyChanged(winrt::event_token const& token) noexcept
    {
        m_MainFrame_PropertyChanged.remove(token);

        return;
    }
MainWindow.xaml.cpp

The first version of PropertyChanged() ATTACHES an event handler which returns a unique event token (If you look into WinUIs implementation in base.h you will find that the event token is something like an iterator for an internal vector which points to the position where all the different event handler are stored). The second version DETACHES an event handler, which is then identified by exactly that event token.

You do not need to call PropertyChanged() yourself, unless you want to add your own separate event handler. WinUI calls the PropertyChanged() function when MainWindow is being instantiated and connects it with its build-in WinUI event handler. You can check this out by placing breakpoints in the functions when you launch the application.

Now we have everything in place, let’s summarize it up:

A connector through which MainWindow (the view) can get the RNA data (the model) from. This connector is our hstrGetRNANbNucleotides() function.

  • A mechanism where the model can inform the view about a change in the data. This is done through our event m_MainFrame_PropertyChanged which is connected with the WinUI framework through an internal event handler which is being attached by a call of PropertyChanged() by the framework.

What still needs to be done

What still needs to be done is to raise an event when the internal data has been changed. That is fairly easy: each time a change to the internal data (=the model) occurs we need to raise an event. That will result in a call of a connector of the model.

For example, if the user performs a paste operation and we convert the text string in the clipboard into nucleotide objects, we need
a) update our internal state which tells the application that there are RNA data available (which is for the enable state of our “Clear” button) and
b) update the number of nucleotides:

//  ------------------------------------------------------------------------------------------------------
/*! \brief  paste clipboard content to the end of the RNA
*   \date   09/15/2025  AGS Start
*/
//  ------------------------------------------------------------------------------------------------------
winrt::Windows::Foundation::IAsyncAction    MainWindow::PasteClipboardToRNA()
{
    winrt::Windows::ApplicationModel::DataTransfer::DataPackageView dataPackageView = nullptr;

    dataPackageView = winrt::Windows::ApplicationModel::DataTransfer::Clipboard::GetContent();

    winrt::hstring  hstrClipBoard = co_await    dataPackageView.GetTextAsync();

		int iPos=0;
    while (iPos < (int)hstrClipBoard.size()) {
			wchar_t c = hstrClipBoard[iPos];
        m_WinUIRNA.CreateAndAddNucleotide(c);
        iPos++;

		}   //  endwhile (iPos < hstrClipBoard.size()) {

    //  notify interested parties
    m_MainFrame_PropertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"bHasRNAData" });
    m_MainFrame_PropertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"hstrGetRNANbNucleotides" });

    co_return;
}
MainWindow.xaml.cpp

If you simply want to update all controls, just call PropertyChangedEventArgs() with an empty string:

m_MainFrame_PropertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"" });
C++

A quick deeper look

Our event in m_MainFrame_PropertyChanged is of type winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler>. If you look once more into base.h you will find that the () operator is being overloaded. Its parameters are a list of arguments. In our code the first argument being passed is “us” (which is the ‘this‘ object) the second is a newly created instance of the Microsoft::UI::Xaml::Data::PropertyChangedEventArgs class which gets a string as a parameter. The WinUI documentation states this string is “the short name of the property that changed”, which is usually a getter function.

Obviously, all we need to do, if a change of data in the ‘model’ occurs is to call the ‘connector’ which supplies the (changed) data.

Observable collections

Remember our first tutorial where we set up the application? We chose an “observable vector” to store the nucleotides of the RNA. The class we chose is winrt::Windows::Foundation::Collections::IObservableVector<T> . The IObservableVector class encapsulates the mechanisms for property changed notifications. It implements its own event internally. If a change to the vector occurs that event will automatically be raised and the view(s) which use the vector will be informed. If you want to go more into detail about the internals, the generated code in clWinUIControl_RNAList.xaml.g.hpp might be a good start.

The take-away from this: if you implement a vector by using the IObservableVector<T> class you do not need to worry about the implementation of property change mechanisms by yourself. The WinUI framework does it for you.

However, if you want to get informed about a change to your vector for a reason, you may add your own delegate by calling the IObservableVector<T> member function VectorChanged().

Once more: Mode=TwoWay

In our example we provided data binding in only one direction: when the internal data are updated the view of the data are updated as well. In data binding we added the connector with the Mode=OneWay.

Let’s look at a short “two-way” example. We add another control which not only shows the number of nucleotides in the RNA but where we also can enter the number of nucleotides. If we enter a number lower than the number of nucleotides in the RNA we will shorten the RNA, if we enter a higher number we will add invalid nucleotides at the end.

Let’s update the user interface first and add that additional control:

<!-- Two way data binding -->
<Grid   Margin="0, 10, 0, 0"    Width="150"
        BorderBrush="{StaticResource  CardStrokeColorDefaultBrush}"
        Background="{StaticResource CardBackgroundFillColorSecondaryBrush}"
        BorderThickness="1"
        CornerRadius="{StaticResource   ControlCornerRadius}"
        HorizontalAlignment="Left">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBlock    Grid.Row="0"    
                  Text="Number of Nucleotides (two way)"    
                  Style="{StaticResource  CaptionTextBlockStyle}"
                  HorizontalAlignment="Center"
                  />
    <TextBox    Grid.Row="1"    
                  Text="{x:Bind   strRNANbNucleotides_Property, Mode=TwoWay}"    
                  HorizontalAlignment="Center"
                  />
</Grid>
MainWindow.xaml

Note the “Mode=TwoWay” parameter in the <TextBox>.

In the IDL we define a separate ‘connector’ which now also has to work both ways. We need to be able to read AND write data from AND to our ‘model’:

namespace How2_PropertyChange_DataBinding
{
    [default_interface]
    runtimeclass MainWindow : Microsoft.UI.Xaml.Window, Microsoft.UI.Xaml.Data.INotifyPropertyChanged
    {
                    MainWindow();

        clWinUIRNA  GetWinUIRNA_Window_Property{get;};
            Boolean bHasRNAClipboardData;
            Boolean bHasRNAData{get;};
            String  hstrGetRNANbNucleotides{get;};
            String  strRNANbNucleotides_Property;

    }
}
MainWindow.idl

Of course, we could now replace the hstrGetRNANbNucleotides() function which does the same as the getter implementation of strRNANbNucleotides_Property(). For readability of the tutorial, we will keep it that way.

We need to raise events for that control in PasteClipboardToRNA() as well as in OnBtnClearRNA_Click():

//  notify interested parties
m_MainFrame_PropertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"bHasRNAData" });
m_MainFrame_PropertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"hstrGetRNANbNucleotides" });
m_MainFrame_PropertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"strRNANbNucleotides_Property" });
C++

Finally, the implementation of the getter/setter:

//  ------------------------------------------------------------------------------------------------------
/*! \brief  property (to be used by XAML): number of nucleotides in the RNA object
*
*           Used for two-way binding
*   \date   09/24/2025  AGS Start
*/
//  ------------------------------------------------------------------------------------------------------
hstring MainWindow::strRNANbNucleotides_Property()
{
    return  to_hstring(this->m_WinUIRNA.Get_obsvecRNA_Property().Size());
}
void MainWindow::strRNANbNucleotides_Property(hstring const& value)
{
    int iSize;

		iSize = _wtoi(value.c_str());

    while ((int)this->m_WinUIRNA.Get_obsvecRNA_Property().Size() < iSize) {
			this->m_WinUIRNA.CreateAndAddNucleotide('?');
    }   //  endwhile (this->m_WinUIRNA.Get_obsvecRNA_Property().Size() < iSize) {

    while ((int)this->m_WinUIRNA.Get_obsvecRNA_Property().Size() > iSize) {
			this->m_WinUIRNA.Get_obsvecRNA_Property().RemoveAtEnd();
		}   //  endwhile (this->m_WinUIRNA.Get_obsvecRNA_Property().Size() > iSize) {


    return;
}
MainWindow.xaml.cpp

Each time we change the value in the <TextBox>, the setter of strRNANbNucleotides_Property() is being called. Remember: since we use an observable vector, there is no need to manually raise an event after we have modified the vector – WinUI does it for us.

What you should know about Mode=OneTime

We talked about the two modes Mode=OneWay and Mode=TwoWay. There is a third mode: Mode=OneTime. This is kind of a ‘static data binding’. If you set Mode=OneTime your getter function will only be called once. That call happens exactly by the time your control is being created. Please keep in mind: your ‘connector’ will NOT BE CALLED ANYMORE. No matter if and when you raise events! And that is a common pitfall: if you forget to set the Mode parameter, WinUI will set it to its default value which is Mode=OneTime.

And I can assure you: hours of developer time (including my own) have been wasted with controls that do not update themselves, because the Mode parameter had not set.

Why is Mode=OneTime available, anyway? Microsoft brings up performance issues. Which is very likely right. The OneTime option simply does not implement any property change notification which of course safes time. However, in my experience, most of the time you do NOT have ‘static’ data binding BUT dynamic data binding, means the internal data change and UI has to reflect to that.

And, by the way, we are already done!

Summary

Let’s summarize the major thoughts:

  • Property changed notifications are being used to synchronize the ‘view’ (the UI in your application) with the ‘model’ (the data of your application).
  • The mechanism for the communication between those two is called ‘data binding’.
  • This mechanism uses WinUI events.
  • To implement data binding between a control and its internal data representation the view needs to implement an event which templates the PropertyChangedEventHandler class. 
  • To enable property change notification, your view (where you implement the event) needs to implement the interface Microsoft.UI.Xaml.Data.INotifyPropertyChanged. That is being done in the IDL. This also requires the implementation of two PropertyChanged() functions.
  • When an internal date has changed you need to raise the data binding event with the name of the property attached to.
  • The Mode property defines the direction of the data binding. Caution: if you do not specify the Mode property it will be set to its default which is Mode=OneTime, which is kind of a static property
  • The “observable collections” (IObservableVector, IObservableMap, …) have all these mechanisms ‘build in’.

WinUI Stuff: How to program Copy & Paste operations (C++)

What is in for you

In this tutorial, I want to talk about copy & paste operations in WinUI3. You will see that invoking a copy & paste operation requires just a few lines of code.

I will stick to the example of handling an RNA molecule with its nucleotides (if this is your first How 2 you are reading, then check the introduction of the first tutorial WinUI Stuff: How to use a Data Template Selector for ListView or GridView Controls (C++) – Part I about the background of the biochemistry we’re simulating). Up to now, the sequence of the RNA has been hardcoded in the program. We will change this and extend the program, so we can copy a nucleotide sequence from a text editor into our RNA using the clipboard.

Let’s start with the user experience (it is a good habit to always start with the user experience). We start the program with a blank screen. To paste the clipboard content, there will be a button “Paste”. The button is disabled if there is nothing useful in the clipboard. When the user clicks the button, the program will copy the nucleotide sequence from the clipboard to the end of our RNA sequence.

As you can see from the screenshot, we also have an additional button “Clear RNA”. It has nothing to do with our copy & paste example, but it is useful for testing. By clicking that button all nucleotides are being removed from the RNA sequence. If the sequence is empty the button is disabled.

If we copy a nucleotide sequence e.g. from the windows notepad to the clipboard…:

… we can paste it using the “Paste at end” button and it will be shown as an RNA:

In this tutorial you will learn about

  • How to use the Clipboard object in the winrt::Windows::ApplicationModel::DataTransfer namespace
  • How to work with asynchronous operations in WinRT
  • How to use property change notifications. BUT: even though you will see some code which is required for property change notifications (their purpose is to inform WinUI to update controls ‘automatically’), I won’t go into detail in this tutorial (it would just get too long). I will explain the property change notification functionality of this code in the next tutorial “How 2: Property Change Notification”. However, for the understanding of the copy & paste mechanism it is not necessary to have that knowledge already. 

You will find the code in my GitHub repositories: https://github.com/AgentSmith-Dev/How2-WinUI

Further reading: https://learn.microsoft.com/en-us/uwp/api/windows.applicationmodel.datatransfer.clipboard?view=winrt-26100

What you should already know

  • You should have an idea how the clipboard in Windows generally works.
  • It might be a good idea if you already have read Part I and Part II of the tutorials “How to make a Data Template Selector”.

The architecture

The overall architecture has already been described in the first tutorial WinUI Stuff: How to use a Data Template Selector for ListView or GridView Controls (C++) – Part I:

App.xaml wrapping MainWindow.xaml wrapping clWinUIContrl_RNAList.xaml wrapping ListView
Architecture of the WinUI3 HowTo „DataTemplateSelector – Part I“

The user defined control clWinUIControl_RNAList wraps up the mechanism to show the RNA. Of course, we could add our two buttons (“Paste”, “Clear”) there – which might be the obvious approach on first sight. But then it would be hard to reuse that control in the future, because it would always come with those buttons in place. So, it might be better to have the controls for operating the RNA in a different control. To keep it simple for this tutorial we let the main window (MainWindow.xaml) handle the two buttons. In a ‘real’ application we would probably have a separate user control inside MainWindow.xml which handled the RNA operation.

But again, for this example, it is ok to have the buttons in MainWindow.xml. Since we have with m_WinUIRNA access to the clWinUIControl_RNAList we won’t have problems updating our internal RNA representation after a user operation. As a matter of fact, you will see that WinUI almost does everything on its own in the background (the magic comes from the IObservableVector<> which we used to implement the RNA).

The user experience

Let’s start with the user experience. We need to add two buttons to MainWindow.xaml:

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
       
        <local:clWinUIControl_RNAList   Grid.Column="0"   GetWinUIRNA_Page_Property="{x:Bind  GetWinUIRNA_Window_Property}"/>

        <StackPanel Grid.Column="1" Orientation="Vertical"  VerticalAlignment="Center">
            <Button x:Name="Btn_Paste"
                    IsEnabled="{x:Bind  bHasRNAClipboardData, Mode=OneWay}"
                    Click="OnBtnPastAtRNAEnd_Click"
                    Width="150"
                    >Paste at end</Button>

            <Button x:Name="Btn_Clear"
                    IsEnabled="{x:Bind  bHasRNAData, Mode=OneWay}"
                    Click="OnBtnClearRNA_Click"
                    Width="150"
                    Margin="0, 10, 0, 0"
                    >Clear RNA</Button>
        </StackPanel>
    </Grid>
MainWindow.xaml

There is nothing special about the XAML code. We have two <Button> controls, one for “Paste”, one for “Clear”. Each button has a function to update it’s ‘enable state’. Button “Paste” calls bHasRNAClipboardData() and Button “Clear“ calls bHasRNAData(). And we attach an event handler to each button which is being called when the button is being clicked.

Again: If you’re not familiar with property change notifications, I recommend (the upcoming) tutorial number 4: “How 2: Property Change Notifications and Data Binding”. The attribute “Mode=One” is for that purpose – for the understanding of the code it is not necessary, and you may ignore it.

Code Behind

The functions bHasRNAClipboardData() and bHasRNAData() are member functions of MainWindow and they will be called from XAML, which means we need to define them in the IDL:

import "clWinUIRNA.idl"; 

namespace How2_CopyNPaste
{
    [default_interface]
    runtimeclass MainWindow : Microsoft.UI.Xaml.Window, Microsoft.UI.Xaml.Data.INotifyPropertyChanged
    {
                    MainWindow();

        clWinUIRNA  GetWinUIRNA_Window_Property{get;};
            Boolean bHasRNAClipboardData;
            Boolean bHasRNAData{get;};

    }
}
MainFrame.idl

(Note: the “base interface” INotifyPropertyChanged is for property change notification and has nothing to do with the copy & past mechanism).

The implementation of both functions is again fairly simple:

C++
//  ------------------------------------------------------------------------------------------------------
/*! \brief  getter/setter (to be used by XAML): are there data in the clipboard
*   \desc
*   \date   09/15/2025  AGS Start
*/
//  ------------------------------------------------------------------------------------------------------

bool MainWindow::bHasRNAClipboardData()
{
    return  m_bHasRNAClipboardData;
}

void MainWindow::bHasRNAClipboardData(bool bHasRNAClipboardData)
{
    if (bHasRNAClipboardData == m_bHasRNAClipboardData) {
    }
    else {
			m_bHasRNAClipboardData = bHasRNAClipboardData;
			//  notify interested parties
        m_MainFrame_PropertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"bHasRNAClipboardData" });
    }   //  endif (bHasRNAClipboardData == m_bHasRNAClipboardData) {

    return;
}
MainFrame.cpp
C++
//  ------------------------------------------------------------------------------------------------------
/*! \brief  property (to be used by XAML): are there data (=nucleotides) in the RNA object
*   \desc
*   \date   09/15/2025  AGS Start
*/
//  ------------------------------------------------------------------------------------------------------
bool MainWindow::bHasRNAData()
{
    return  this->m_WinUIRNA.Get_obsvecRNA_Property().Size() > 0;
}
MainFrame.cpp

The state of the “Paste” button reflects if there is useful information in the clipboard by using the boolean member “m_bHasRNAClipboardData”. The “Clear” button reflects if the RNA sequence contains nucleotides straight from the vector which contains the nucleotide objects.

Time to talk about Copy & Paste

Now, we have everything in place to implement Copy & Paste functionality.

First of all, we have to include the required header:

C++
#include    <winrt/Windows.ApplicationModel.DataTransfer.h>
MainFrame.cpp

If you don’t do so, you will get strange errors like error C3779: ‚winrt::Windows::ApplicationModel::DataTransfer::Clipboard::ContentChanged‘: a function that returns ‚auto‘ cannot be used before it is defined.

That is one of the biggest weaknesses of WinUI: the sometimes very poor output of error messages. You might get an error message, but that might be far from having something to do with the root cause of the error.

We want to know if the user has copied something into the clipboard. That requires to update the “Paste” button. For that, we create an event which will be raised by the framework every time the content of the clipboard changes. We add the initialization in the constructor:

C++
 MainWindow::MainWindow()
{
    m_EventToken_ClipboardChanged = winrt::Windows::ApplicationModel::DataTransfer::Clipboard::ContentChanged({ this, &MainWindow::OnClipboardChanged });

    UpdateClipboardState();

}
MainFrame.cpp

We connect the event token with the call back function OnClipboardChanged(). Each time the clipboard changes, the framework will call that callback function. There is no need to define that function in the IDL. Since the calling mechanism of that function is not by a COM mechanism (it is not via XAML, etc.): you do not need to define it in the IDL. 

The token is being released in the desctructor:

C++
MainWindow::~MainWindow()
{
    winrt::Windows::ApplicationModel::DataTransfer::Clipboard::ContentChanged(m_EventToken_ClipboardChanged);
}
MainFrame.cpp

The OnClipboardChanged() function calls our UpdateClipboardState() function which we already used in the constructor to do a proper initialization of the UI:

C++
//  ------------------------------------------------------------------------------------------------------
/*! \brief  event handler for changes of the clipboard - called by the framework
*   \date   09/15/2025  AGS Start
*	\param  sender
*	\param  e
*/
//  ------------------------------------------------------------------------------------------------------
void MainWindow::OnClipboardChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& e)
{
    sender;
    e;

    UpdateClipboardState();

    return;
}
MainFrame.cpp
C++
//  ------------------------------------------------------------------------------------------------------
/*! \brief  update the clipboard of the app
*           
*           for formats available in the clipboard \see https://learn.microsoft.com/en-us/uwp/api/windows.applicationmodel.datatransfer.standarddataformats.text?view=winrt-22621
*   \date   09/15/2025  AGS Start
*/
//  ------------------------------------------------------------------------------------------------------
void MainWindow::UpdateClipboardState()
{
    bool    bHasTextInClipboard = true;

    winrt::Windows::ApplicationModel::DataTransfer::DataPackageView dataPackageView=nullptr;

    dataPackageView = winrt::Windows::ApplicationModel::DataTransfer::Clipboard::GetContent();
    hstring hstrTextFormat = winrt::Windows::ApplicationModel::DataTransfer::StandardDataFormats::Text();

    if (dataPackageView.Contains(hstrTextFormat)) {
        bHasTextInClipboard = true;
        OutputDebugStringW(L"Clipboad contains text\n");
    }
    else {
        bHasTextInClipboard = false;
        OutputDebugStringW(L"Clipboad empty\n");
    }

    bHasRNAClipboardData(bHasTextInClipboard);

    return;
}
MainFrame.cpp

The clipboard object provides a DataPackageView object which we get by calling GetContent(). That object contains all the information about the current state of the clipboard as well as the content of the clipboard itself (we will use that in a minute). As you know, the Windows clipboard is able to provide multiple formats. We expect our RNA sequence in a text format. Therefore, we get the sequence of the clipboard by calling the StandardDataFormats::Text() function. The call of the bHasRNAClipboardData() function will memorize the state of the clipboard (see above).

Pasting the clipboard

When the user clicks the “Paste” button, the framework calls our event handler (see XAML above) OnBtnPastAtRNAEnd_Click(). From there we call our PasteClipboardToRNA() function:

C++
//  ------------------------------------------------------------------------------------------------------
/*! \brief  paste clipboard content to the end of the RNA
*   \date   09/15/2025  AGS Start
*/
//  ------------------------------------------------------------------------------------------------------
winrt::Windows::Foundation::IAsyncAction    MainWindow::PasteClipboardToRNA()
{
    winrt::Windows::ApplicationModel::DataTransfer::DataPackageView dataPackageView = nullptr;

    dataPackageView = winrt::Windows::ApplicationModel::DataTransfer::Clipboard::GetContent();

    winrt::hstring  hstrClipBoard = co_await    dataPackageView.GetTextAsync();

		int iPos=0;
    while (iPos < (int)hstrClipBoard.size()) {
			wchar_t c = hstrClipBoard[iPos];
        m_WinUIRNA.CreateAndAddNucleotide(c);
        iPos++;

		}   //  endwhile (iPos < hstrClipBoard.size()) {

    //  notify interested parties
    m_MainFrame_PropertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"bHasRNAData" });

    co_return;
}
MainFrame.cpp

Let us ignore the function type ISyncAction and the return statement co_return for a moment. Inside the function, we find a similar code as we already had in the UpdateClipboardState() function: we declare a DataPackageView object and ask for an instance by calling GetContent(). The GetTextAsync() will fetch the (text) content of the clipboard. And we have it handy in the string variable hstrClipBoard.

The while loop iterates through this text string and asks our clWinUIRNA object by calling CreateAndAddNucleotide() to a create nucleotide for each character. The function is smart enough to create an “error” nucleotide if the character is unknown.

The updating of the visual appearance of the RNA is done by the framework.

That’s it. Copy & Paste operation performed.

The asynchronous part of the story

The developers of WinUI tried to create a UI framework which feels very fluently. To achieve this, operations that might take the slightest moment are being executed in an asynchronous operation. That means the (=UI thread) does not execute the operation instantly but memorizes it for later execution and returns without execution immediately.

That is exactly what happens here: GetTextAsync() is an operation that will be memorized and the thread returns immediately. But where does the thread return to? Of course, not to the next line, which would the while() loop. That would iterate through the string, which is not initialized by that time. No, the thread goes straight to the “co_return” statement and exits the function.

GetTextAsync() will be executed when there is time to do so. After this had been done, the code which follows the GetTextAsync() statement will be executed. Means, the while() loop will be executed and then it will be iterated about the string that had been fetched. The asynchronous operation terminates when the co_return is being reached.

You can observe this behavior yourself by setting a breakpoint “A” at the return statement at the end of the OnBtnPastAtRNAEnd_Click() function and another breakpoint “B” at the while() statement in the PasteClipboardToRNA() function. You will see that first the breakpoint “A” is being hit and then “B” is being hit, later on.

Summary

Let’s summarize the major thoughts:

  • We get access to the clipboard through a winrt::Windows::ApplicationModel::DataTransfer::DataPackageView object which we get by the winrt::Windows::ApplicationModel::DataTransfer::Clipboard::GetContent() function.
  • Upon the initialization of a control where copy & paste is required, we create an event handle to get “ContentChanged” notifications and connect it with a callback function.
  • Each time a change in the clipboard occurs (this includes of course when another program copies content into the clipboard) that callback function is being called by the framework. 
  • Each time a control is being operated which invokes a copy & paste operation we use the DataPackageView object.
  • Accessing the content of the clipboard has to be done by using an asynchronous operation. That keeps WinUI applications fluent.
  • Do not forget the #include statement in the cpp file where you implement the winrt::Windows::ApplicationModel::DataTransfer::Clipboard namespace.

And that is our final result (the sequence depends of course on the paste operation you performed):

Where to go from here

We used quite a few “property changed” mechanisms in this tutorial to update the UI upon a change. We will look into this mechanisms in the next turial.

WinUI Stuff: How to use a Data Template Selector for ListView or GridView Controls (C++) – Part II

Part II

What is in for you?

In Part I of this “How To” we learned how easy it is to create a list using the ListView control of WinUI. But the promising headline was “how to use a Data Template selector” and honestly, yes, we used a data template, but we did not use a SELECTOR at all. So, finally we learned how to set up a list rather quickly and we saw how easy it is to style the items using XAML. But I promised that it is also easy to style each item INDIVIDUALLY. And that is exactly where a data template SELECTOR comes into play.

In this tutorial you will learn about:

  • Reusing a <DataTemplate> within a <ContentControl>
  • Using static resources as defined in the WinUI3 gallery
  • Using a data template selector

What we will reach within this How To:

You should have read Part I of this how to, since we will extend that code we will mainly rely on the concepts there.

You will find the code in my GitHub repositories: https://github.com/AgentSmith-Dev/How2-WinUI

Further reading: https://learn.microsoft.com/en-us/windows/apps/design/controls/data-template-selector

The principle

When we used the <ListView> attribute ItemTemplate in Part I we established a 1:1 relationship between an item in the control and the <DataTemplate> to be used for rendering.

If we have more than one template available, we additionally need something in between. That is where the data template selector comes into play:

The look of the nucleotides

Let’s start, where good concepts usually start: with the customer experience. Let’s design the look (&feel) of the nucleotides first. Currently we focus on the part “look” – there is not so much “feel” (yet!) in – we will improve that in one of the later “How Tos”. But even though we want to have a different look for each nucleotide we do not want to have them entirely different. Therefore, we keep the main look of a nucleotide from the first part. Each nucleotide is represented by a large capital letter:

<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<!--    A Nucleotide -->
<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
  <DataTemplate x:Key="resNucleotide" x:DataType="local:clWinUINucleotide">
      <StackPanel>
          <TextBlock Text="{x:Bind hstrSymbol_Property, Mode=OneWay}" 
                     Margin="5"
                     Style="{StaticResource   TitleTextBlockStyle}"
          />
      </StackPanel>
  </DataTemplate>

Have you noticed (already in Part I): there is no reason for the use of the <StackPanel> control. However, I usually start with a wrapping <StackPanel> control even though there might be only one control inside of it. But usually, an additional control will be added in the development process sooner or later and therefore I use to have it from the beginning. But if you were wondering about the purpose of the <StackPanel> in the design of the nucleotide in Part I, here is the answer: there is no reason.

In Part I each nucleotide had a border around its capital letter. You probably have noticed that this has been gone. Well, it’s time to start individualization and each nucleotide type will get its individual border with its individual border color. Let’s start with Adenine and we define an additional <DataTemplate> just for “Adenine”:

<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<!--    The Adenine nucleotide -->
<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<DataTemplate x:Key="resNucleotide_Adenine" x:DataType="local:clWinUINucleotide">
    <Border BorderThickness="2, 2, 2, 2"    
            Background="Orange"   
            BorderBrush="{StaticResource    ControlElevationBorderBrush}">
        <StackPanel Orientation="Vertical">
            <ContentControl ContentTemplate="{StaticResource resNucleotide}" 
                            Content="{x:Bind}"   
                            HorizontalAlignment="Center"
                            Width="Auto"/>
            <TextBlock    Width="Auto" Text="Adenine"
                          HorizontalAlignment="Center"
                          Style="{StaticResource   CaptionTextBlockStyle}"
                          Margin="2"
            />
        </StackPanel>

    </Border>

</DataTemplate>

The XAML is pretty much straightforward and there is not much to say about. Maybe with one exception: the <ContentControl>. I always think of the <ContentControl> as makro which “embeds” another control. That’s what happens here. The attribute ContentTemplate “embeds” our StaticResource resNucleotide. This way, we have one resource which will be used by each nucleotide, and they will all look the same.

We add the three remaining <DataTemplate>s for Cytosine, Guanine and Uracil. Check the code on GitHub if you want to have a deeper look at them.

And finally, we add a further <DataTemplate> for the defects in the RNA. That one is entirely different from the other templates:

<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<!--    The Error nucleotide -->
<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<DataTemplate x:Key="resNucleotide_Error" x:DataType="local:clWinUINucleotide">
    <Border BorderThickness="2, 2, 2, 2"    
            Background="{StaticResource SystemFillColorCriticalBackgroundBrush}"   
            BorderBrush="{StaticResource    SystemFillColorCriticalBrush}">
            <StackPanel Orientation="Vertical">
                <TextBlock  Width="Auto" Text="Error"
                            HorizontalAlignment="Center"
                            Margin="2"
                            Style="{StaticResource    BodyStrongTextBlockStyle}"
                />
            </StackPanel>
    </Border>
</DataTemplate>

Of course, there is no <ContentControl> here, because there is no need to show a letter for a nucleotide.

Let’s talk about styling according to Windows guidelines: WinUI Gallery Dev

You have probably noticed the StaticResources like SystemFillColorCriticalBackgroundBrush, ControlElevationBorderBrush and many more. Theres are resources defined by Windows. You may find them in a file called Generic.XAML which is automatically included by the framework. But where do you know from, which resources to use. Here comes the WinUI3 Gallery Dev into play.

The WinUI Gallery is part of the Windows App SDK (you can even find the source code on GitHub) and it gives examples of the controls available in WinUI. The WinUI gallery also includes a “Design Guidance”. There you can find the colors, the typography, etc. which shall be used for the different purposes.

Connect the <ListView> with the <DataTemplate>

Now, we have a <DataTemplate> for each nucleotide and for a defect in place. How do we tell the <ListView> about the <DataTemplate>s? In Part I where we only had one <DataTemplate> and for that we used the ItemTemplate attribute of the <ListView>:

<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<!--    Control to show the RNA (TAKEN FROM PART1) -->
<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<ListView   x:Name="List_RNA"
            ItemsSource="{x:Bind  GetWinUIRNA_Page_Property.Get_obsvecRNA_Property, Mode=OneWay}"
            ItemTemplate="{StaticResource resNucleotide}"   
            Width="100"
>
</ListView>
How we attached to the data template to the list view in Part I

Since we now have five different <DataTemplate>s to be chosen from, we need some additional magic, a “Selector” is required which tells the <ListView> which template to use:

<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<!--    Control to show the RNA -->
<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<ListView   x:Name="List_RNA"
        ItemsSource="{x:Bind
                   GetWinUIRNA_Page_Property.Get_obsvecRNA_Property, Mode=OneWay}"
        ItemTemplateSelector="{StaticResource
                              Key_WinUI_DataTemplateSelector_RNA_Nucleotids}"
        Width="100"
                
>
</ListView>
XAML

You can see the DataTemplate-Selector is implemented as a resource (you see it by the “StaticResource” statement) and it obviously can be found it under “Key_WinUI_DataTemplateSelector_RNA_Nucleotids”:

<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<!--    Data template selector to be used to display the nucleotids -->
<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<local:clWinUI_DataTemplateSelector_RNA_Nucleotids
    
        x:Key="Key_WinUI_DataTemplateSelector_RNA_Nucleotids"
    
        Adenine_Property="{StaticResource resNucleotide_Adenine}"
        Cytosine_Property="{StaticResource resNucleotide_Cytosine}"
        Guanine_Property="{StaticResource resNucleotide_Guanine}"
        Uracil_Property="{StaticResource resNucleotide_Uracil}"
        Error_Property="{StaticResource resNucleotide_Error}"
/>
clWinUIControl_RNAList.xaml

The name of the resource is defined in its x:key statement. Further on you will find the names of the five resources for the nucleotides which we created above: resNucleotide_Adenine, resNucleotide_Cytosine, resNucleotide_Guanine, resNucleotide_Uracil, resNucleotide_Error. Each of these templates is connected to a code behind function: Adenine_Property, Cytosine_Property, Guanine_Property, Uracil_Property, Error_Property.

The data template selector is implemented like a custom control. All the logic needed is wrapped up in a custom WinUI class, called clWinUI_DataTemplateSelector_RNA_Nucleotids.

How to implement our data template selector

namespace How2_PartII_DataTemplateSelector
{
        runtimeclass    clWinUI_DataTemplateSelector_RNA_Nucleotids : Microsoft.UI.Xaml.Controls.DataTemplateSelector
        {
            clWinUI_DataTemplateSelector_RNA_Nucleotids();

            Microsoft.UI.Xaml.DataTemplate    Adenine_Property;
            Microsoft.UI.Xaml.DataTemplate    Cytosine_Property;
            Microsoft.UI.Xaml.DataTemplate    Guanine_Property;
            Microsoft.UI.Xaml.DataTemplate    Uracil_Property;
            Microsoft.UI.Xaml.DataTemplate    Error_Property;

        }
}
clWinUI_DataTemplateSelector_RNA_Nucleotids.idl

Obviously, there is not much to do. We need to define the code behind to gain access the data templates. Everything else is brought by the base interface Microsoft.UI.Xaml.Controls.DataTemplateSelector.

What is the purpose of these functions? The framework will call the setter version during the initialization of the control which uses the template selector. The setter gets an “interface” to the data template itself. The setter only needs to store it for later use (here the setter and the getter for Adenine):

C++
//  ------------------------------------------------------------------------------
/*! \brief  getter/setter for adenine data template
*   \date   08/26/2025  AGS Start
*/
//  ------------------------------------------------------------------------------
winrt::Microsoft::UI::Xaml::DataTemplate clWinUI_DataTemplateSelector_RNA_Nucleotids::Adenine_Property()
{
    return  m_DataTemplate_Adenin_Property;
}
void clWinUI_DataTemplateSelector_RNA_Nucleotids::Adenine_Property(winrt::Microsoft::UI::Xaml::DataTemplate const& value)
{
    m_DataTemplate_Adenin_Property=value;

    return;
}

These interfaces need to be stored somewhere. Therefore, we add a member for each item in the header file of the selector:

C++
protected:
        Microsoft::UI::Xaml::DataTemplate m_DataTemplate_Adenin_Property;
        Microsoft::UI::Xaml::DataTemplate m_DataTemplate_Cytosine_Property;
        Microsoft::UI::Xaml::DataTemplate m_DataTemplate_Guanine_Property;
        Microsoft::UI::Xaml::DataTemplate m_DataTemplate_Uracil_Property;
        Microsoft::UI::Xaml::DataTemplate m_DataTemplate_Error_Property;
clWinUI_DataTemplateSelector_RNA_Nucleotids.h

If you have look at the header file you will find a function that has been created by the framework: SelectTemplateCore(). The framework calls this function whenever an item in the <ListView> control (some for <GridView> control) is being rendered. The function gets an IInspectable interface of the item. In a first step we need to “cast” the IInspectable interface to a nucleotide:

C++
        clWinUINucleotide   rWinUINucleotide;
        if ((item.try_as<clWinUINucleotide>(rWinUINucleotide))) {
clWinUI_DataTemplateSelector_RNA_Nucleotids.cpp

Then we need to decide which <DataTemplate> is to be used. Our nucleotides have a “Type” property so we can easily implement a switch() statement. Depending on the type we return the appropriate interface to the <DataTemplate> which we stored above:

C++
//  ------------------------------------------------------------------------------
/*! \brief  called by the framework: the the template according the item to be
            inspected
*   \date   08/26/2025  AGS Start
*/
//  ------------------------------------------------------------------------------
winrt::Microsoft::UI::Xaml::DataTemplate clWinUI_DataTemplateSelector_RNA_Nucleotids::SelectTemplateCore(winrt::Windows::Foundation::IInspectable const& item)
{
    winrt::Microsoft::UI::Xaml::DataTemplate  rDataTemplate;

    clWinUINucleotide   rWinUINucleotide;
    if ((item.try_as<clWinUINucleotide>(rWinUINucleotide))) {
        switch (rWinUINucleotide.enNucleotide_Type_Property()) {
        case    winrt::How2_PartII_DataTemplateSelector::enNucleotide_Type::Adenine: {
            rDataTemplate = Adenine_Property();
            break;
        }
        case    winrt::How2_PartII_DataTemplateSelector::enNucleotide_Type::Cytosine: {
            rDataTemplate = Cytosine_Property();
            break;
        }
        case    winrt::How2_PartII_DataTemplateSelector::enNucleotide_Type::Guanine: {
            rDataTemplate = Guanine_Property();
            break;
        }
        case    winrt::How2_PartII_DataTemplateSelector::enNucleotide_Type::Uracil: {
            rDataTemplate = Uracil_Property();
            break;
        }
        default: {
            rDataTemplate = Error_Property();
        }
        }   //  endswitch (rWinUINucleotide.enNucleotide_Type_Property()) {
    }
    else{
        rDataTemplate = Error_Property();

		}   //  endif (item.try_as<clWinUINucleotide>(rWinUINucleotide)) {e


    return  rDataTemplate;
}
clWinUI_DataTemplateSelector_RNA_Nucleotids.cpp

And that’s it (almost, see the final trick below).

The two versions of SelectTemplateCore()

If you look at the code, you will find two versions of SelectTemplateCore()which are just slightly different. One version has an additional parameter dp which is a DependencyObject.

So, where do we implement our switch() statement? Or to be precise, which version is called by the framework? Microsoft’s online documentation is not very clear about it. It turns out that in most of the cases, if you have a <ListView> control (<GridView> contro) from which you want to select a <DataTemplate> the version with one parameter is the right one. The version with two parameters is never being called.

If you have a <ContentControl> which uses a data template selector with the attribute ContentTemplateSelector=”…” instead, then the version with two parameters is being called. You can test this yourself if you make things a bit more complicated and use a <ContentControl> with a ContentTemplateSelector instead of using a DataTemplateSelector directly:

C++
<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<!--    Control to show the RNA / Deprecated version using ContentControl -->
<!--    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
<ListView   x:Name="List_RNA"
            ItemsSource="{x:Bind       
              GetWinUIRNA_Page_Property.Get_obsvecRNA_Property, Mode=OneWay}"
            Width="100"
>
  <ListView.ItemTemplate>
    <DataTemplate>
      <ContentControl  Content="{Binding}"  
                       ContentTemplateSelector="{StaticResource    
                          Key_WinUI_DataTemplateSelector_RNA_Nucleotids}"
      />
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

 
Use the two-parameter-version of SelectCore()

The output of the RNA remains the same.

Don’t forget the final trick

If you compile the project you usually get an error like:

error C2039: 'clWinUI_DataTemplateSelector_RNA_Nucleotids': is not a member of 'winrt::How2_PartII_DataTemplateSelector::implementation'
error message

The code behind of the custom control which implements the data template selector needs to be aware of the implementation of the data template selector. Therefore, we add an include statement to the header of clWinUIControl_RNAList.h.

C++
#pragma once

#include "clWinUIControl_RNAList.g.h"

#include    "clWinUIRNA.h"
#include    "clWinUI_DataTemplateSelector_RNA_Nucleotids.h"
clWinUIControl_RNAList.h

Unfortunately, this is not very obvious. WinRT creates a lot of code by itself. As a matter of fact, we program only a small part of our WinUI classes. The rest is being added automatically by the framework. And it is obvious that the control needs know about the data template selector, unfortunately we can see it in “our” code. We only can see it by looking at the generated code.

Summary

Let’s summarize the major thoughts:

  • While we have a 1:1 relationship between item and template if we use the ItemTemplate attribute, we create a 1:n relationship by using the attribute ItemTemplateSelector. That selector makes the connection between the <ListViewItem> and the <DataTemplate>s.
  • A data template selector is defined as a separate XAML-resource by its key.
  • The data template selector is implemented through the interface Microsoft.UI.Xaml.Controls.DataTemplateSelector. Each <DataTemplate> we want to use gets its own getter/setter.
  • These setters are called by the framework during initialization of the <ListView>. Each setter gets an interface to the <DataTemplate>. The data template selector instance needs to store the interfaces.
  • We need to override the SelectTemplateCore() function which is being called by the framework when an item is going to be rendered to select the appropriate <DataTemplate>.
  • Don’t forget the #include statement in the header of the control (it’s being mentioned here because it might safe you hours of headache while searching for the problem…)

And that is our final result:

Where to go from here

We will extend our little RNA program a bit and add some functionality (the “feel” we talked in the introduction). Property change notifications are the mechanism to synchronize the UI and the internal data structures. In the end we will add some drag&drop functionality.

WinUI Stuff: How to use a Data Template Selector for ListView or GridView Controls (C++) – Part I

Part I

What is in for you?

The ListView control in XAML (<ListView>) is a great control to easily display elements in a list (let’s talk about its – I’d say even more powerful – sibling GridView in part II of the how to). Especially programmers who are used to select MFC for the UI can tell how hard it is to realize an individual styling for rows in a list control. Or even more challenging for the rows individually, depending on the content to be displayed. Using WinUI3 it is fairly easy to create the styling in XAML: you provide a template for an entry in a row and WinUI will do the rest.

This “How to” is split into two parts: in the first part I’d like to explain the basics how to make a list using the <ListView> control. In the second part we will extend the “How to” and add individual row design based on the type of the content to be displayed in a row.

In this tutorial you will learn about:

  • The WinUI control <ListView>
  • Using Data Templates in XAML with <DataTemplate>
  • Using a user defined control with <UserControl> (or <Page>)
  • Using the WinUI specific vector class “observable vector” (IObservableVector)

What we will reach within this How To:

You should be familiar with WinUI and how to create an app, as well as how to use auto generated code (from the MIDL compiler), etc. I will not discuss these steps in detail. For styling purposes, I use the WinUI Gallery.

I will use Visual Studio 2022 and C++ (version 17). By the time I write this how to, Microsoft is up to the (stable) Version 1.7250606001 of the WindowsAppSDK. I am using Windows 11.

You will find the code in my GitHub repositories: https://github.com/AgentSmith-Dev/How2-WinUI

An example from Biochemistry: display an RNA sequence

RNA (ribonucleic acid) is the name of a set of large molecules which play an essential role in in a lot of biological functions, mainly in the cell of plants, animals and human beings. Each RNA consists of four different types of molecules, called “nucleotides”: Adenosine (A), Cytosine (C), Guanine (G), Uracil (U). The sequence of these nucleotides is crucial for the functionality of the RNA.

Learn more about RNA: https://en.wikipedia.org/wiki/RNA

An RNA sequence is perfect for a list view example: we want to show an RNA sequence, just by displaying its nucleotids.

In the example we will display the microRNA rno-miR-21 from Rattus norvegicu, which is: 5′-UAGCUUAUCAGACUGAUGUUGA-3′.

Bring the biochemistry to C++

For the backbone of our example, we need two different classes:

clWinUINucleotide: represents a nucleotid. For the sake of simplicity its only member is the type of the nucleotide which we wrap up in an enumeration enNucleotide_Type.

clWinUIRNA: represents an RNA sequence. Since an RNA sequence is always linear and does not have branches, a vector is perfect to store the instances of clWinUINucleotide. Unfortunately, WinUI is not really good in supporting the C++ std library. Instead WinUI is supplying (I’d almost say as we’re used to from Microsoft) its own version of a vector class (yes, there may be good reasons, why std::vector can’t be used in winrt, but it is kind of annoying anyway). We will use the template class IObservableVector which you will find in the winrt::Windows::Foundation::Collections namespace. Note that this class is NOT in the Microsoft namespace as you might expect since WinUI3 is using this namespace instead of the Windows namespace which WinUI2 did. Another vector class would be IVector. But we will use IObservableVector which is more convenient to be used within a list view control. We’ll talk about that later.

App Architecture and backbone

The architecture of our app is straightforward. The base implementation has been created by the project wizard of Visual Studio 2022:

App.xaml wrapping MainWindow.xaml wrapping clWinUIContrl_RNAList.xaml wrapping ListView

UI architecture of the app

We have an App class (and exactly one instance) which holds everything together. The app class creates an instance of the MainWindow class during startup. So, this class is a good place to store our RNA. There, you will find the member m_WinUIRNA which is an instance of clWinUIRNA. To display the RNA, MainWindow implements a user control clWinUIControl_RNAList.xaml. This is where you find the <ListView> control which will display our RNA with its nucleotides.

Further down you will see that clWinUIControl_RNAList requires access to the RNA. Therefore, MainWindow forwards a reference to the RNA to clWinUIControl_RNAList. This is being done in the XAML if MainWindow when clWinUIControl_RNAList is instantiated:

<Window
    x:Class="How2_DataTemplateSelector.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:How2_DataTemplateSelector"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="How2_DataTemplateSelector - Part I">

    <local:clWinUIControl_RNAList   GetWinUIRNA_Page_Property="{x:Bind  GetWinUIRNA_Window_Property}"/>
</Window>

Create the app

The main focus of this how to is to how to implement data templates and data template selectors. Therefore, I won’t go too much into detail about setting up the app from the beginning. Let’s just have a quick look at the major steps.

We use the project wizard to create a blank app:

The wizard will create the backbone of the app for you:

Let’s add our two biochemistry classes to the app. We first create the idl files, then let the compiler create the .cpp and .h files (remember: you will find those in the ${ProjectDir}/GeneratedFiles/sources/).

Finally, we need a (XAML) class to display the RNA. We create a user defined control which we will implement in Mainframe.xaml. Let’s the project wizard do the main work.

(if you look at my examples you will find that I did not use the “User Control” template but the “Blank Page” template, therefore you will find a <Page> control instead of a <UserControl>. For our example here it does not make a difference).

When this is done your solution explorer should have everything in place:

Implementing the user control to display the RNA

Let us implement the user control. Open the file clWinUIControl_RNAList.xaml and add the XAML sections.

In the resources section we will add a <DataTemplate> which will define the look of a single row:

<Page.Resources>
        <!--  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
        <!--    A Nucleotide -->
        <!--  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
        <DataTemplate x:Key="resNucleotide" x:DataType="local:clWinUINucleotide">
            <Border BorderThickness="2, 2, 2, 2"    
                    Background="{StaticResource CardBackgroundFillColorDefaultBrush}"   
                    BorderBrush="{StaticResource    ControlElevationBorderBrush}"
                    >
                <StackPanel Orientation="Vertical"  HorizontalAlignment="Center">
                    <TextBlock Text="{x:Bind hstrSymbol_Property, Mode=OneWay}" 
                        Margin="5"
                        Style="{StaticResource   TitleTextBlockStyle}"
                    />
                </StackPanel>
            </Border>

        </DataTemplate>
    </Page.Resources>

The <ListView> itself is rather short:

    <!--  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
    <!--    Control to show the RNA -->
    <!--  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++    -->
    <ListView   x:Name="List_RNA"
                ItemsSource="{x:Bind     GetWinUIRNA_Page_Property.Get_obsvecRNA_Property, Mode=OneWay}"
                ItemTemplate="{StaticResource resNucleotide}"   
                Width="100"
    >
    </ListView>

What do we have here? We have the x:Name which does not have any function (yet). It is there for documentation purposes as well as for extensibility. Let’s skip the ItemsSource attribute for a moment and go to the ItemTemplate attribute. It tells the <ListView> control to use the template specified in the resource section with the name “resNucleotide” (which we defined before). When the program runs each <DataTemplate> will get a clWinUINucleotide-object as you can see in the x:DataType attribute when being rendered. How is this being done? Now we come to the real interesting part: the <ListView> control has an ItemsSource attribute which tells the control where to get the data from. We’re using dynamic binding with the x:Bind operation. The x:Bind statement is being followed by the name of the interface “GetWinUIRNA_Page_Property”. This is a “code behind” function in the clWinUIControl_RNAList implementation. It is declared in the .idl file:

namespace How2_DataTemplateSelector
{
    [default_interface]
    runtimeclass clWinUIControl_RNAList : Microsoft.UI.Xaml.Controls.Page
    {
        clWinUIControl_RNAList();
        clWinUIRNA  GetWinUIRNA_Page_Property;
    }
}

That function returns a clWinUIRNA object. So, using this function we have access to our RNA. That means access to the vector with the nucleotids is needed. This is exactly what the (see in the XAML above) interface “Get_obsvecRNA_Property” does. This is again a “code behind” function, now in the clWinUIRNA implementation. It is declared in the .idl file:

namespace	How2_DataTemplateSelector
{
	runtimeclass    clWinUIRNA : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
	{
		clWinUIRNA();
		
		Windows.Foundation.Collections.IObservableVector<clWinUINucleotide>      Get_obsvecRNA_Property{ get; };

        void CreateAndAddNucleotide(Char wcSymbol);

	}
}

That is why we used IObservableVector to store our nucleotides. The XAML controls can directly access such a vector which is very convenient. As a programmer you do not need any overhead to use an IObservableVector together with XAML controls. Everything which is required is in the class built in. One pitfall because it is often forgotten and ends with an exception being thrown: you need to initialize the instance of the IObservableVector class (which is valid for all classes of the observable collection classes). 

C++
//  ------------------------------------------------------------------------------
/*! \brief  Constructor for clWinUIRNA
 *  \detail
 *  \date   08/22/2025  AGS Start
 */
//  ------------------------------------------------------------------------------
    clWinUIRNA::clWinUIRNA()
    {
        // Initialize the RNA vector
        m_obsvecRNA = winrt::single_threaded_observable_vector<winrt::How2_DataTemplateSelector::clWinUINucleotide>();
    }

That’s it. Summarized, the expression ItemsSource=“{x:Bind    GetWinUIRNA_Page_Property.Get_obsvecRNA_Property, Mode=OneWay}“ tells the <ListView> control that it can find a vector by calling GetWinUIRNA_Page_Property() and then Get_obsvecRNA_Property(). For each object in the vector the data template is being rendered. 

The Parameter “Mode=OneWay” advises the WinUI framework to update the <ListView> control if there is a change in the vector. Very convenient, isn’t it?!

Summary

Let us summarize the major thoughts

  • Each nucleotide of an RNA is represented by an instance of the clWinUINucleotide class.
  • We have a class which implements the RNA itself: clWinUIRNA.
  • The RNA-class has an IObservableVector which holds the nucleotides. The IObservableVector can be accessed from “outside” using the interface function Get_obsvecRNA_Property().
  • The visual representation of the RNA is done in the user control clWinUIControl_RNAList.xaml. The instance of the class has a reference to the RNA class clWinUIRNA. This reference can be accessed from “outside” using the interface function GetWinUIRNA_Page_Property().
  • The XAML of the user control clWinUIControl_RNAList.xaml uses the <ListView> control to display the nucleotides in a list. 
  • The visual representation of each nucleotide is defined in a <DataTemplate> resource in XAML.
  • The <ListView> control may access the IObservableVector using dynamic binding through its ItemSource property calling the interfaces GetWinUIRNA_Page_Property() and Get_obsvecRNA_Property().

And that is our final result:

Where to go from here?

In the second part of this how to, we will implement a “data template selector”. Using a data template selector we can have individual data templates. Using such a selector each nucleotide can have its own visual representation. And we will also add an “error nucleotide” which is a representation of invalid nucleotides.