-1

Edit 3: Complete project if anybody wants to check

https://github.com/sumnoon/ProgressRingSample_WinUI3

Edit 2: Added Complete code

Edit: Added the code I am trying to use as sample

I am trying to add progress ring in WinUI 3 sample app. The plan is simple, add a button and when clicked show a progress ring.

I have intialized the progress ring similar to this. This is the XAML code for ProgressRingControl.xaml,

<UserControl
x:Class="progress_sample.ProgressRingControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:progress_sample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid VerticalAlignment="Stretch"
      HorizontalAlignment="Stretch">

    <Grid x:Name="IndeterminateProgressPanel"
        Background="LightGray"
        Opacity=".5">
    </Grid>

    <ProgressRing x:Name="IndeterminateProgress"
                  Foreground="Red"
                  Width="65"
                  Height="65" />
</Grid>

And this the code for ProgressRingControl.xaml.cs,

    using Microsoft.UI.Xaml;
    using Microsoft.UI.Xaml.Controls;
    using Microsoft.UI.Xaml.Controls.Primitives;
    using Windows.UI.Core;

namespace progress_sample
{
    public sealed partial class ProgressRingControl : UserControl
    {
        private Popup popup = null;
        public ProgressRingControl()
        {
            this.InitializeComponent();
            SystemNavigationManager.GetForCurrentView().BackRequested += ProgressRingControl_BackRequested;
            Window.Current.CoreWindow.SizeChanged += CoreWindow_SizeChanged;

            popup = new Popup
            {
                Child = this
            };
        }

        public static ProgressRingControl Instance
        {
            get { return Singleton<ProgressRingControl>.Instance; }
        }

        private void CoreWindow_SizeChanged(CoreWindow sender, Windows.UI.Core.WindowSizeChangedEventArgs args)
        {
            UpdateUI();
        }

        private void UpdateUI()
        {
            var bounds = Window.Current.Bounds;
            this.Width = bounds.Width;
            this.Height = bounds.Height;
        }

        private void ProgressRingControl_BackRequested(object sender, BackRequestedEventArgs e)
        {
            //HideProgressRing();
        }
        public void ShowProgressRing()
        {
            IndeterminateProgress.IsActive = true;
            popup.IsOpen = true;
            UpdateUI();
        }

        public void HideProgressRing()
        {
            if (popup.IsOpen)
            {
                IndeterminateProgress.IsActive = false;
                popup.IsOpen = false;
                SystemNavigationManager.GetForCurrentView().BackRequested -= ProgressRingControl_BackRequested;
                Window.Current.CoreWindow.SizeChanged -= CoreWindow_SizeChanged;
            }
        }
    }
}

I am calling this From MainWindow.xaml using a button in such a way that when button is clicked the progress ring should show for 1000ms than hide. I have added MainWindow.xaml and MainWindows.xaml.cs code also, MainWindow.xaml code:

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

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
    </StackPanel>
</Window>

And MainWindow.xaml.cs code is given below,

using Microsoft.UI.Xaml;
using System.Threading.Tasks;


namespace progress_sample
{

    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }

        private async void myButton_Click(object sender, RoutedEventArgs e)
        {
            ProgressRingControl.Instance.ShowProgressRing();
            await Task.Delay(1000);
            ProgressRingControl.Instance.HideProgressRing();
        }
    }
}

But during initialization of ProgressRingControl, it is crashing and gives this error,

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Any help?

Sumnoon
  • 71
  • 7
  • Make sure you're using the latest WinUI3 (1.1.5 as of today) – Simon Mourier Sep 30 '22 at 14:47
  • Sorry, I have updated but still crashing. – Sumnoon Oct 03 '22 at 09:12
  • You should post a full reproducible project: https://stackoverflow.com/help/minimal-reproducible-example – Simon Mourier Oct 03 '22 at 10:12
  • @SimonMourier I have added a full reproducible example. Hope this can be used for reproduction. Thank you for your suggestion btw. – Sumnoon Oct 03 '22 at 11:12
  • This is not a fully reproducible example, it's far from compiling, and there's this Singleton thing that is a bad smell. You should not use singletons for Xaml windows, controls, UI elements, etc. Just create one when you need one. – Simon Mourier Oct 03 '22 at 17:47
  • Hi @SimonMourier I have added complete project link in my post with edit. I think it should satisfy your query. – Sumnoon Oct 04 '22 at 18:08
  • 1
    SystemNavigationManager & Window.Current are only available in UWP, not in WinUI3 see this for some more background https://github.com/microsoft/microsoft-ui-xaml/issues/4174 .In fact Window.Current being null is a sign that you're not in UWP somehow, and most GetForCurrentView methods - for almost any type - don't work outside of UWP. – Simon Mourier Oct 04 '22 at 18:21

1 Answers1

1

This is one of the easiest way that I can think of.

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

    <StackPanel>
        <ToggleButton
            x:Name="ToggleButtonControl"
            Checked="ToggleButtonControl_Changed"
            Content="Toggle Button"
            Unchecked="ToggleButtonControl_Changed" />
        <ProgressRing
            x:Name="ProgressRingControl"
            IsIndeterminate="False" />
    </StackPanel>

</Window>
using Microsoft.UI.Xaml;

namespace ProgressRingTest;

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
    }

    private void ToggleButtonControl_Changed(object sender, RoutedEventArgs e)
    {
        this.ProgressRingControl.IsIndeterminate = this.ToggleButtonControl.IsChecked ?? false;
    }
}

UPDATE

This is another way to do to achieve what you said in your comments. In this case, I'm creating an UserControl. I'm also using the CommunityToolkit.Mvvm NuGet package to demonstrate a way to run a process in the back.

PopupWithProgressRing.xaml.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Threading.Tasks;

namespace PopupTest;

public sealed partial class PopupWithProgressRing : UserControl
{
    public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(
        nameof(IsOpen),
        typeof(bool),
        typeof(PopupWithProgressRing),
        new PropertyMetadata(false, OnIsOpenPropertyChanged));

    public PopupWithProgressRing()
    {
        this.InitializeComponent();
    }

    public bool IsOpen
    {
        get => (bool)GetValue(IsOpenProperty);
        set => SetValue(IsOpenProperty, value);
    }

    private static async void OnIsOpenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is PopupWithProgressRing self &&
            e.NewValue is bool value)
        {
            // The ProgressRing won't freezes in the 2nd run without this delay.
            // Probably a WinUI 3 bug. I'll report to the WinUI 3 later.
            await Task.Delay(100);
            self.ProgressRingControl.IsIndeterminate = value;
        }
    }
}

PopupWithProgressRing.xaml

<UserControl
    x:Class="PopupTest.PopupWithProgressRing"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:PopupTest"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Popup
            x:Name="ProgressRingPopup"
            VerticalAlignment="Top"
            IsOpen="{x:Bind IsOpen, Mode=OneWay}">
            <StackPanel>
                <ProgressRing x:Name="ProgressRingControl" />
            </StackPanel>
        </Popup>
    </Grid>
</UserControl>

MainWindowViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Threading.Tasks;

namespace PopupTest;

public partial class MainWindowViewModel : ObservableObject
{
    [RelayCommand]
    // The CommunityToolkit.Mvvm will automatically generate
    // a "RunProcessCommand" for you.
    private async Task RunProcess()
    {
        await Task.Delay(3000);
    }
}

MainWindow.xaml.cs

using Microsoft.UI.Xaml;

namespace PopupTest;

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
    }

    public MainWindowViewModel ViewModel { get; } = new MainWindowViewModel();
}

MainWindow.xaml

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

    <Grid>
        <ToggleButton
            Command="{x:Bind ViewModel.RunProcessCommand}"
            Content="Run process" />

        <local:PopupWithProgressRing
            x:Name="ProgressRingPopup"
            IsOpen="{x:Bind ViewModel.RunProcessCommand.IsRunning, Mode=OneWay}" />
    </Grid>
</Window>
Andrew KeepCoding
  • 7,040
  • 2
  • 14
  • 21
  • Hi @Andrew, I am sorry that I couldn't properly convey my question in the first iteration of my post. I have edited now. Thanks for your suggestion. – Sumnoon Oct 05 '22 at 07:20
  • Hi @Sumnoon. I saw your code but IMHO you need to reconsider a lot in your design. I'm assuming that some process (1 sec) runs when you click a button and you want to show a ``Popup`` with a ``ProgressRing`` while this process is running, then you want to close this ``Popup`` when the process. Am I correct? And this process, of course, is outside your ``ProgressRingControl``? – Andrew KeepCoding Oct 05 '22 at 07:50
  • Hi @Andrew. Yes you are correct. BTW, I have shared the complete sample code link that I have used for reproduction. You can check that too if you want. However the crash issue occurs due to SystemNavigationManager & Window.Current not supported in WinUI 3. Is there any alternate for this in regard of my case? – Sumnoon Oct 06 '22 at 04:53
  • I updated my answer. Let me know if you have any questions. – Andrew KeepCoding Oct 06 '22 at 06:26