diff --git a/WinUIGallery/Helpers/TabViewHelper.cs b/WinUIGallery/Helpers/TabViewHelper.cs
index 7ab110cec..7dc6deb24 100644
--- a/WinUIGallery/Helpers/TabViewHelper.cs
+++ b/WinUIGallery/Helpers/TabViewHelper.cs
@@ -96,5 +96,11 @@ public static void PopulateTabViewContextMenu(MenuFlyout menuFlyout)
             };
             menuFlyout.Items.Add(moveRightItem);
         }
+
+        // If the context menu ended up with no items at all, then we'll prevent it from being shown.
+        if (menuFlyout.Items.Count == 0)
+        {
+            menuFlyout.Hide();
+        }
     }
 }
diff --git a/WinUIGallery/Helpers/UIHelper.cs b/WinUIGallery/Helpers/UIHelper.cs
index c222e0f08..171cd20b5 100644
--- a/WinUIGallery/Helpers/UIHelper.cs
+++ b/WinUIGallery/Helpers/UIHelper.cs
@@ -63,4 +63,21 @@ static public void AnnounceActionForAccessibility(UIElement ue, string annouceme
         peer.RaiseNotificationEvent(AutomationNotificationKind.ActionCompleted,
                                     AutomationNotificationProcessing.ImportantMostRecent, annoucement, activityID);
     }
+
+    public static T GetParent<T>(DependencyObject child) where T : DependencyObject
+    {
+        DependencyObject current = child;
+
+        while (current != null)
+        {
+            if (current is T parent)
+            {
+                return parent;
+            }
+
+            current = VisualTreeHelper.GetParent(current);
+        }
+
+        return null;
+    }
 }
diff --git a/WinUIGallery/Helpers/Win32WindowHelper.cs b/WinUIGallery/Helpers/Win32WindowHelper.cs
index a01a1f204..ca7283c92 100644
--- a/WinUIGallery/Helpers/Win32WindowHelper.cs
+++ b/WinUIGallery/Helpers/Win32WindowHelper.cs
@@ -6,8 +6,8 @@ namespace WinUIGallery.Helpers;
 
 internal class Win32WindowHelper
 {
-    private static WinProc newWndProc = null;
-    private static nint oldWndProc = nint.Zero;
+    private WinProc newWndProc = null;
+    private nint oldWndProc = nint.Zero;
 
     private POINT? minWindowSize = null;
     private POINT? maxWindowSize = null;
diff --git a/WinUIGallery/Samples/ControlPages/TabViewPage.xaml b/WinUIGallery/Samples/ControlPages/TabViewPage.xaml
index ce0914485..46ff36e2e 100644
--- a/WinUIGallery/Samples/ControlPages/TabViewPage.xaml
+++ b/WinUIGallery/Samples/ControlPages/TabViewPage.xaml
@@ -104,7 +104,8 @@
                             <TabViewItem
                                 Content="{x:Bind DataContent}"
                                 Header="{x:Bind DataHeader}"
-                                IconSource="{x:Bind DataIconSource}" />
+                                IconSource="{x:Bind DataIconSource}"
+				ContextFlyout="{x:Bind TabViewContextMenu}"  />
                         </DataTemplate>
                     </TabView.TabItemTemplate>
                 </TabView>
diff --git a/WinUIGallery/Samples/SamplePages/TabContentSampleControl.xaml b/WinUIGallery/Samples/SamplePages/TabContentSampleControl.xaml
index 3cad36ffd..b6d5ce43b 100644
--- a/WinUIGallery/Samples/SamplePages/TabContentSampleControl.xaml
+++ b/WinUIGallery/Samples/SamplePages/TabContentSampleControl.xaml
@@ -13,7 +13,7 @@
         <TextBlock Text="{Binding}" Style="{ThemeResource TitleTextBlockStyle}" />
         <TextBlock Text="Drag the Tab outside of the window to spawn a new window." Style="{ThemeResource SubtitleTextBlockStyle}" />
         <TextBlock Text="Notice that the state of the Tab is maintained in the new window. For example, if you toggle the ToggleSwitch ON, it will remain ON in the new window." Style="{ThemeResource BodyTextBlockStyle}" />
-        <ToggleSwitch x:Name="ControlToggle" Header="Turn on ProgressRing" Margin="0,8" />
-        <ProgressRing IsActive="{x:Bind ControlToggle.IsOn, Mode=OneWay}" HorizontalAlignment="Left" />
+        <ToggleSwitch x:Name="ControlToggle" Header="Turn on ProgressRing" Margin="0,8" IsOn="{x:Bind IsInProgress, Mode=TwoWay}" />
+        <ProgressRing IsActive="{x:Bind IsInProgress, Mode=OneWay}" HorizontalAlignment="Left" />
     </StackPanel>
 </UserControl>
diff --git a/WinUIGallery/Samples/SamplePages/TabContentSampleControl.xaml.cs b/WinUIGallery/Samples/SamplePages/TabContentSampleControl.xaml.cs
index 1d9aefc3b..795f1dd5a 100644
--- a/WinUIGallery/Samples/SamplePages/TabContentSampleControl.xaml.cs
+++ b/WinUIGallery/Samples/SamplePages/TabContentSampleControl.xaml.cs
@@ -1,3 +1,4 @@
+using Microsoft.UI.Xaml;
 using Microsoft.UI.Xaml.Controls;
 
 namespace WinUIGallery.SamplePages;
@@ -8,4 +9,13 @@ public TabContentSampleControl()
     {
         this.InitializeComponent();
     }
+    public bool IsInProgress
+    {
+        get { return (bool)GetValue(IsInProgressProperty); }
+        set { SetValue(IsInProgressProperty, value); }
+    }
+
+    public static readonly DependencyProperty IsInProgressProperty = DependencyProperty.Register("IsInProgress", typeof(bool), typeof(TabContentSampleControl), new PropertyMetadata(false));
+
+ 
 }
diff --git a/WinUIGallery/Samples/SamplePages/TabViewWindowingSamplePage.xaml b/WinUIGallery/Samples/SamplePages/TabViewWindowingSamplePage.xaml
index 94d1d0053..cd5bf69d1 100644
--- a/WinUIGallery/Samples/SamplePages/TabViewWindowingSamplePage.xaml
+++ b/WinUIGallery/Samples/SamplePages/TabViewWindowingSamplePage.xaml
@@ -9,22 +9,36 @@
     mc:Ignorable="d">
 
     <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
-        <TabView
-            x:Name="Tabs"
-            VerticalAlignment="Stretch"
-            AddTabButtonClick="Tabs_AddTabButtonClick"
-            CanTearOutTabs="True"
-            ExternalTornOutTabsDropped="Tabs_ExternalTornOutTabsDropped"
-            ExternalTornOutTabsDropping="Tabs_ExternalTornOutTabsDropping"
-            TabCloseRequested="Tabs_TabCloseRequested"
-            TabTearOutRequested="Tabs_TabTearOutRequested"
-            TabTearOutWindowRequested="Tabs_TabTearOutWindowRequested">
+        <TabView 
+		 x:Name="Tabs" 
+                 VerticalAlignment="Stretch"
+                 AddTabButtonClick="Tabs_AddTabButtonClick"
+                 TabCloseRequested="Tabs_TabCloseRequested"
+                 CanTearOutTabs="True"
+                 TabTearOutWindowRequested="Tabs_TabTearOutWindowRequested"
+                 TabTearOutRequested="Tabs_TabTearOutRequested"
+                 ExternalTornOutTabsDropping="Tabs_ExternalTornOutTabsDropping"
+                 ExternalTornOutTabsDropped="Tabs_ExternalTornOutTabsDropped"
+                 TabItemsSource="{x:Bind TabItemDataList}">
             <TabView.TabStripHeader>
                 <Grid x:Name="ShellTitleBarInset" Background="Transparent" />
             </TabView.TabStripHeader>
             <TabView.TabStripFooter>
                 <Grid x:Name="CustomDragRegion" Background="Transparent" />
             </TabView.TabStripFooter>
+            <TabView.TabItemTemplate>
+                <DataTemplate x:DataType="local:TabItemData">
+                    <TabViewItem Header="{x:Bind Header}">
+                        <TabViewItem.ContextFlyout>
+                            <MenuFlyout Opening="TabViewContextMenu_Opening" />
+                        </TabViewItem.ContextFlyout>
+                        <TabViewItem.IconSource>
+                            <SymbolIconSource Symbol="Placeholder" />
+                        </TabViewItem.IconSource>
+                        <local:TabContentSampleControl DataContext="{x:Bind Content}" IsInProgress="{x:Bind IsInProgress, Mode=TwoWay}" />
+                    </TabViewItem>
+                </DataTemplate>
+            </TabView.TabItemTemplate>
         </TabView>
     </Grid>
 </Page>
diff --git a/WinUIGallery/Samples/SamplePages/TabViewWindowingSamplePage.xaml.cs b/WinUIGallery/Samples/SamplePages/TabViewWindowingSamplePage.xaml.cs
index 28015b537..fc0aa376a 100644
--- a/WinUIGallery/Samples/SamplePages/TabViewWindowingSamplePage.xaml.cs
+++ b/WinUIGallery/Samples/SamplePages/TabViewWindowingSamplePage.xaml.cs
@@ -1,16 +1,56 @@
 using System.Linq;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Microsoft.UI.Dispatching;
+using Microsoft.UI.Input;
 using Microsoft.UI.Xaml;
 using Microsoft.UI.Xaml.Controls;
 using Microsoft.UI.Xaml.Media;
+using Windows.System;
 using WinUIGallery.Helpers;
+using System;
 
 namespace WinUIGallery.SamplePages;
 
+public class TabItemData : DependencyObject
+{
+    public SymbolIconSource IconSource { get; set; } = new SymbolIconSource()
+    {
+        Symbol = Symbol.Placeholder
+    };
+    public string Header
+    {
+        get { return (string)GetValue(HeaderProperty); }
+        set { SetValue(HeaderProperty, value); }
+    }
+
+    public string Content
+    {
+        get { return (string)GetValue(ContentProperty); }
+        set { SetValue(ContentProperty, value); }
+    }
+
+    public bool IsInProgress
+    {
+        get { return (bool)GetValue(IsInProgressProperty); }
+        set { SetValue(IsInProgressProperty, value); }
+    }
+
+    public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(string), typeof(TabItemData), new PropertyMetadata(""));
+    public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(string), typeof(TabItemData), new PropertyMetadata(""));
+    public static readonly DependencyProperty IsInProgressProperty = DependencyProperty.Register("Content", typeof(bool), typeof(TabItemData), new PropertyMetadata(false));
+}
+
 public sealed partial class TabViewWindowingSamplePage : Page
 {
     private const string DataIdentifier = "MyTabItem";
+    private static readonly List<Window> windowList = [];
+    private static Window tabTearOutWindow = null;
+
     private Win32WindowHelper win32WindowHelper;
-    private Window tabTearOutWindow = null;
+
+    private readonly ObservableCollection<TabItemData> tabItemDataList = [];
+    public ObservableCollection<TabItemData> TabItemDataList => tabItemDataList;
 
     public TabViewWindowingSamplePage()
     {
@@ -31,6 +71,40 @@ private void TabViewWindowingSamplePage_Loaded(object sender, RoutedEventArgs e)
         currentWindow.ExtendsContentIntoTitleBar = true;
         currentWindow.SetTitleBar(CustomDragRegion);
         CustomDragRegion.MinWidth = 188;
+
+        if (!windowList.Contains(currentWindow))
+        {
+            windowList.Add(currentWindow);
+
+            // We can have a window we're dragging in two different ways: either we created a new window
+            // for tearing out purposes, or we're dragging an existing window.
+            // If we created a new window, tabTearOutWindow will be set to that window.
+            // Otherwise, it won't be set to anything, so we should set it to the window we're currently dragging.
+            var inputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(currentWindow.AppWindow.Id);
+
+            double scaleAdjustment = currentWindow.Content.XamlRoot.RasterizationScale;
+            double titleContentWidth = CustomDragRegion.Width;
+            double titleContentHeight = CustomDragRegion.Height;
+            Windows.Graphics.RectInt32 titleBarRect = new Windows.Graphics.RectInt32(0, 0, (int)Math.Round(titleContentWidth * scaleAdjustment), (int)Math.Round(titleContentHeight * scaleAdjustment));
+            inputNonClientPointerSource.SetRegionRects(NonClientRegionKind.Passthrough, new Windows.Graphics.RectInt32[] { titleBarRect });
+            inputNonClientPointerSource.EnteredMoveSize += (s, args) =>
+            {
+                if (tabTearOutWindow == null)
+                {
+                    tabTearOutWindow = currentWindow;
+                }
+            };
+
+            inputNonClientPointerSource.ExitedMoveSize += (s, args) =>
+            {
+                tabTearOutWindow = null;
+            };
+
+            currentWindow.Closed += (s, args) =>
+            {
+                windowList.Remove(currentWindow);
+            };
+        }
     }
 
     public void LoadDemoData()
@@ -38,27 +112,15 @@ public void LoadDemoData()
         // Main Window -- add some default items
         for (int i = 0; i < 3; i++)
         {
-            Tabs.TabItems.Add(new TabViewItem() { IconSource = new Microsoft.UI.Xaml.Controls.SymbolIconSource() { Symbol = Symbol.Placeholder }, Header = $"Item {i}", Content = new TabContentSampleControl() { DataContext = $"Page {i}" } });
+            TabItemDataList.Add(new TabItemData() { IconSource = new Microsoft.UI.Xaml.Controls.SymbolIconSource() { Symbol = Symbol.Placeholder }, Header = $"Item {i}", Content = $"Page {i}" });
         }
 
         Tabs.SelectedIndex = 0;
     }
 
-    public void AddTabToTabs(TabViewItem tab)
-    {
-        Tabs.TabItems.Add(tab);
-    }
-
     private void Tabs_TabTearOutWindowRequested(TabView sender, TabViewTabTearOutWindowRequestedEventArgs args)
     {
-        var newPage = new TabViewWindowingSamplePage();
-
-        tabTearOutWindow = WindowHelper.CreateWindow();
-        tabTearOutWindow.ExtendsContentIntoTitleBar = true;
-        tabTearOutWindow.Content = newPage;
-        tabTearOutWindow.AppWindow.SetIcon("Assets/Tiles/GalleryIcon.ico");
-        newPage.SetupWindowMinSize(tabTearOutWindow);
-
+        tabTearOutWindow = CreateNewWindow();
         args.NewWindowId = tabTearOutWindow.AppWindow.Id;
     }
 
@@ -68,13 +130,9 @@ private void Tabs_TabTearOutRequested(TabView sender, TabViewTabTearOutRequested
         {
             return;
         }
-
-        var newPage = (TabViewWindowingSamplePage)tabTearOutWindow.Content;
-
-        foreach (TabViewItem tab in args.Tabs.Cast<TabViewItem>())
+        if (tabItemDataList.Count > 1)
         {
-            GetParentTabView(tab)?.TabItems.Remove(tab);
-            newPage.AddTabToTabs(tab);
+            MoveDataItems(TabItemDataList, GetTabItemDataList(tabTearOutWindow), args.Items, 0);
         }
     }
 
@@ -85,64 +143,174 @@ private void Tabs_ExternalTornOutTabsDropping(TabView sender, TabViewExternalTor
 
     private void Tabs_ExternalTornOutTabsDropped(TabView sender, TabViewExternalTornOutTabsDroppedEventArgs args)
     {
-        int position = 0;
-
-        foreach (TabViewItem tab in args.Tabs.Cast<TabViewItem>())
-        {
-            GetParentTabView(tab)?.TabItems.Remove(tab);
-            sender.TabItems.Insert(args.DropIndex + position, tab);
-            position++;
-        }
+        MoveDataItems(GetTabItemDataList(tabTearOutWindow), TabItemDataList, args.Items, args.DropIndex);
     }
 
-    private TabView GetParentTabView(TabViewItem tab)
+    private static Window CreateNewWindow()
     {
-        DependencyObject current = tab;
+        var newPage = new TabViewWindowingSamplePage();
+        var window = WindowHelper.CreateWindow();
+        window.SystemBackdrop = new MicaBackdrop();
+        window.ExtendsContentIntoTitleBar = true;
+        window.Content = newPage;
+        window.AppWindow.SetIcon("Assets/Tiles/GalleryIcon.ico");
+        newPage.SetupWindowMinSize(window);
+        return window;
+    }
 
-        while (current != null)
+    private static void MoveDataItems(ObservableCollection<TabItemData> source, ObservableCollection<TabItemData> destination, object[] dataItems, int index)
+    {
+        foreach (object tabItemData in dataItems)
         {
-            if (current is TabView tabView)
-            {
-                return tabView;
-            }
+            source.Remove((TabItemData)tabItemData);
+            destination.Insert(index, (TabItemData)tabItemData);
 
-            current = VisualTreeHelper.GetParent(current);
+            index++;
         }
-
-        return null;
     }
 
-    private TabViewItem CreateNewTVI(string header, string dataContext)
+    private static TabView GetTabView(Window window)
     {
-        var newTab = new TabViewItem()
-        {
-            IconSource = new SymbolIconSource()
-            {
-                Symbol = Symbol.Placeholder
-            },
-            Header = header,
-            Content = new TabContentSampleControl()
-            {
-                DataContext = dataContext
-            }
-        };
+        var tabViewPage = (TabViewWindowingSamplePage)window.Content;
+        return tabViewPage.Tabs;
+    }
 
-        return newTab;
+    private static ObservableCollection<TabItemData> GetTabItemDataList(Window window)
+    {
+        var tabViewPage = (TabViewWindowingSamplePage)window.Content;
+        return tabViewPage.TabItemDataList;
     }
 
     private void Tabs_AddTabButtonClick(TabView sender, object args)
     {
-        var tab = CreateNewTVI("New Item", "New Item");
-        sender.TabItems.Add(tab);
+        TabItemDataList.Add(new TabItemData() { Header = "New Item", Content = "New Item" });
     }
 
     private void Tabs_TabCloseRequested(TabView sender, TabViewTabCloseRequestedEventArgs args)
     {
-        sender.TabItems.Remove(args.Tab);
+        TabItemDataList.Remove((TabItemData)args.Item);
 
-        if (sender.TabItems.Count == 0)
+        if (TabItemDataList.Count == 0)
         {
             WindowHelper.GetWindowForElement(this).Close();
         }
     }
+
+    private void TabViewContextMenu_Opening(object sender, object e)
+    {
+        // The contents of the context menu depends on the state of the application, so we'll build it dynamically.
+        MenuFlyout contextMenu = (MenuFlyout)sender;
+
+        // We'll first put the generic tab view context menu items in place.
+        TabViewHelper.PopulateTabViewContextMenu(contextMenu);
+
+        var tabViewItem = (TabViewItem)contextMenu.Target;
+        ListView tabViewListView = UIHelper.GetParent<ListView>(tabViewItem);
+        var window = WindowHelper.GetWindowForElement(tabViewItem);
+
+        if (tabViewListView == null)
+        {
+            return;
+        }
+
+        var tabItemDataList = GetTabItemDataList(window);
+        var tabDataItem = tabViewListView.ItemFromContainer(tabViewItem);
+
+        // Second, we'll include menu items to move this tab to those windows.
+        MenuFlyoutSubItem moveSubItem = new() { Text = "Move tab to" };
+
+        // If there are at least two tabs in this window, we'll include the option to move the tab to a new window.
+        // This option doesn't make sense if there is only one tab, because in that case the source window would have no tabs left,
+        // and we would effectively be just moving the tab from one window with only one tab to another window with only one tab,
+        // leaving us in the same state as we started in.
+        if (tabItemDataList.Count > 1)
+        {
+            MenuFlyoutItem newWindowItem = new() { Text = "New window", Icon = new SymbolIcon(Symbol.NewWindow) };
+
+            newWindowItem.Click += (s, args) =>
+            {
+                var newWindow = CreateNewWindow();
+                MoveDataItems(tabItemDataList, GetTabItemDataList(newWindow), [tabDataItem], 0);
+
+
+                // Activating the window and setting its selected item hit a failed assert if the content hasn't been loaded yet,
+                // so we'll defer these for a tick to allow that to happen first.
+                DispatcherQueue.TryEnqueue(() =>
+                {
+                    newWindow.Activate();
+                    GetTabView(newWindow).SelectedItem = tabDataItem;
+                });
+            };
+
+            moveSubItem.Items.Add(newWindowItem);
+        }
+
+        // If there are other windows that exist, we'll include the option to move the tab to those windows.
+        List<MenuFlyoutItem> moveToWindowItems = [];
+
+        foreach (Window otherWindow in windowList)
+        {
+            if (window == otherWindow)
+            {
+                continue;
+            }
+
+            var windowTabItemDataList = GetTabItemDataList(otherWindow);
+
+            if (windowTabItemDataList.Count > 0)
+            {
+                string moveToWindowItemText = $"Window with \"{windowTabItemDataList[0].Header}\"";
+
+                if (windowTabItemDataList.Count > 1)
+                {
+                    int remainingTabCount = windowTabItemDataList.Count - 1;
+                    moveToWindowItemText += $" and {remainingTabCount} other tab{(remainingTabCount == 1 ? "" : "s")}";
+                }
+
+                MenuFlyoutItem moveToWindowItem = new() { Text = moveToWindowItemText, Icon = new SymbolIcon(Symbol.BackToWindow) };
+                moveToWindowItem.Click += (s, args) =>
+                {
+                    MoveDataItems(tabItemDataList, windowTabItemDataList, [tabDataItem], windowTabItemDataList.Count);
+
+                    // If removing the tab from its current tab view will leave no tabs remaining, then we'll close the tab view's window.
+                    if (tabItemDataList.Count == 0)
+                    {
+                        window.Close();
+                    }
+
+                    // Activating the window and setting its selected item hit a failed assert if the content hasn't been loaded yet,
+                    // so we'll defer these for a tick to allow that to happen first.
+                    DispatcherQueue.TryEnqueue(() =>
+                    {
+                        otherWindow.Activate();
+                        GetTabView(otherWindow).SelectedItem = tabDataItem;
+                    });
+                };
+                moveToWindowItems.Add(moveToWindowItem);
+            }
+        }
+
+        // Only include a separator if we're going to be including at least one move-to-window item.
+        if (moveToWindowItems.Count > 0)
+        {
+            contextMenu.Items.Add(new MenuFlyoutSeparator());
+        }
+
+        foreach (MenuFlyoutItem moveToWindowItem in moveToWindowItems)
+        {
+            moveSubItem.Items.Add(moveToWindowItem);
+        }
+
+        // Only include the move-to sub-item if it has any items.
+        if (moveSubItem.Items.Count > 0)
+        {
+            contextMenu.Items.Add(moveSubItem);
+        }
+
+        // If the context menu ended up with no items at all, then we'll prevent it from being shown.
+        if (contextMenu.Items.Count == 0)
+        {
+            contextMenu.Hide();
+        }
+    }
 }
diff --git a/WinUIGallery/WinUIGallery.csproj b/WinUIGallery/WinUIGallery.csproj
index ec0f7fea9..264b8d47b 100644
--- a/WinUIGallery/WinUIGallery.csproj
+++ b/WinUIGallery/WinUIGallery.csproj
@@ -644,4 +644,8 @@
             </_NoneWithTargetPath>
         </ItemGroup>
     </Target>
+    <!-- Needed in order for running unpackaged to work properly. -->
+    <Target Name="CopyContentToOutputDirectory" AfterTargets="Build">
+        <Copy SourceFiles="@(Content)" DestinationFiles="@(Content->'$(OutDir)%(Identity)')" SkipUnchangedFiles="true" Condition="!$([System.IO.Path]::IsPathRooted('%(Identity)'))" />
+    </Target>
 </Project>