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.

Veröffentlicht in Blog und verschlagwortet mit , , , .