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:

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.xamlThere 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:
// ------------------------------------------------------------------------------------------------------
/*! \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// ------------------------------------------------------------------------------------------------------
/*! \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.cppThe 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:
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
MainFrame.cppIf 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:
MainWindow::MainWindow()
{
m_EventToken_ClipboardChanged = winrt::Windows::ApplicationModel::DataTransfer::Clipboard::ContentChanged({ this, &MainWindow::OnClipboardChanged });
UpdateClipboardState();
}
MainFrame.cppWe 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:
MainWindow::~MainWindow()
{
winrt::Windows::ApplicationModel::DataTransfer::Clipboard::ContentChanged(m_EventToken_ClipboardChanged);
}
MainFrame.cppThe OnClipboardChanged() function calls our UpdateClipboardState() function which we already used in the constructor to do a proper initialization of the UI:
// ------------------------------------------------------------------------------------------------------
/*! \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// ------------------------------------------------------------------------------------------------------
/*! \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.cppThe 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:
// ------------------------------------------------------------------------------------------------------
/*! \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.cppLet 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.