UWP-执行长视图模型创建并保持UI响应性

问题描述 投票:0回答:1

我已经建立了这个例子来说明我的问题。我需要创建一个层次结构以显示在树视图中,将树视图绑定到视图模型。在VB6的美好时光中,我会使用DoEvents来解锁UI,但在这里我无法理解如何进行操作。

在VS19上,创建一个空白的UWP项目并调用TestLoadSync,然后将其复制并粘贴到文件中:

App.xaml.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace TestLoadSync
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </su
    /// mmary>
    sealed partial class App : Application
    {
        public List<PropertyModel> _Properties;
        public List<ImageModel> _ImagesHirerachy;

        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
            _Properties = new List<PropertyModel>();
            _ImagesHirerachy = new List<ImageModel>();
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {
            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                rootFrame.NavigationFailed += OnNavigationFailed;

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }

            if (e.PrelaunchActivated == false)
            {
                if (rootFrame.Content == null)
                {
                    // When the navigation stack isn't restored navigate to the first page,
                    // configuring the new page by passing required information as a navigation
                    // parameter
                    rootFrame.Navigate(typeof(MainPage), e.Arguments);
                }
                // Ensure the current window is active
                Window.Current.Activate();
            }
        }

        /// <summary>
        /// Invoked when Navigation to a certain page fails
        /// </summary>
        /// <param name="sender">The Frame which failed navigation</param>
        /// <param name="e">Details about the navigation failure</param>
        void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            //TODO: Save application state and stop any background activity
            deferral.Complete();
        }
    }
}

MainPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace TestLoadSync
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private ObservableCollection<PropertyViewModel> _PropVM;
        public MainPage()
        {
            DataLayer aDAL = new DataLayer();
            _PropVM = new ObservableCollection<PropertyViewModel>();
            this.InitializeComponent();
            ProgB.Maximum = 1;
            aDAL.loadData();
            Debug.WriteLine(((App)App.Current)._Properties.Count());
            Debug.WriteLine(((App)App.Current)._ImagesHirerachy.Count());
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ProgB.Value = 0;
            ProgB.Maximum = ((App)App.Current)._Properties.Count() + 1;
            foreach (PropertyModel aProperty in ((App)App.Current)._Properties)
            {
                ProgB.Value++;
                _PropVM.Add(new PropertyViewModel(aProperty));
            }
            ProgB.Value = ProgB.Maximum;
        }
    }
}

MainPage.xaml

<Page
    x:Class="TestLoadSync.MainPage"
    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:TestLoadSync"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">

    <Grid>
        <Button
            Width="296"
            Height="143"
            Margin="245,214,0,0"
            VerticalAlignment="Top"
            Click="Button_Click"
            Content="Button" />
        <ProgressBar
            x:Name="ProgB"
            Width="296"
            Height="82"
            Margin="245,82,0,0"
            HorizontalAlignment="Left"
            VerticalAlignment="Top" />

    </Grid>
</Page>

在项目上添加新的...-类-调用它:DataLayer.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestLoadSync
{
    public class PropertyModel
    {
        public int id;
        public PropertyModel(int _id)
        {
            id = _id;
        }
    }
    public class ImageModel
    {
        public int id;
        public int property;
        public int parent;
        public string desc;
        public ImageModel(int _id, int _property, int _parent, string _desc)
        {
            id = _id;
            property = _property;
            parent = _parent;
            desc = _desc;
        }
    }

    class PropertyViewModel
    {
        private PropertyModel _Property;
        List<ImageViewModel> _Images;
        public PropertyViewModel()
        {
        }
        public PropertyViewModel(PropertyModel aProperty)
        {
            List<ImageModel> _SubImages;
            _Property = aProperty;

            Debug.WriteLine("Property: " + aProperty.id);


            _Images = new List<ImageViewModel>();

            _SubImages = ((App)App.Current)._ImagesHirerachy
            .Where(x => x.property == aProperty.id && x.parent == 0)
            .ToList();

            foreach (ImageModel aImage in _SubImages)
            {
                _Images.Add(new ImageViewModel(aImage, 1));
            }
        }
    }

    class ImageViewModel
    {
        ImageModel _Image;
        List<ImageViewModel> _Images;

        public ImageViewModel()
        {
        }
        public ImageViewModel(ImageModel aImage, int level)
        {
            List<ImageModel> _SubImages;
            _Image = aImage;
            string aS = new string('-', level);

            Debug.WriteLine("          " + aS + aImage.id);

            _Images = new List<ImageViewModel>();

            _SubImages = ((App)App.Current)._ImagesHirerachy
            .Where(x => x.parent == aImage.id && x.property == aImage.property)
            .ToList();

            foreach (ImageModel aSImage in _SubImages)
            {
                _Images.Add(new ImageViewModel(aSImage, ++level));
            }
        }
    }

    class DataLayer
    {
        private int maxProperties = 1000;
        private int maxSubItems = 100;
        public void loadData()
        {
            for (int i = 0; i < maxProperties; i++)
            {
                ((App)App.Current)._Properties.Add(new PropertyModel(i));
            }
            for (int i = 0; i < maxSubItems; i++)
            {
                for (int j = 0; j < (i > maxSubItems / 2 ? maxSubItems / 2 : i); j++)
                {
                    ((App)App.Current)._ImagesHirerachy.Add(new ImageModel(maxProperties + i * (maxSubItems / 2) + j, i, 0, "-" + (((App)App.Current)._ImagesHirerachy.Count() + 1).ToString()));
                }
            }
            for (int i = 0; i < maxSubItems; i++)
            {
                for (int j = 0; j < (i > maxSubItems / 4 ? maxSubItems / 4 : i); j++)
                {
                    ((App)App.Current)._ImagesHirerachy.Add(new ImageModel(maxProperties*2+ i * (maxSubItems/2) + j, i, maxProperties + i * (maxSubItems / 2) + j, "--" + (((App)App.Current)._ImagesHirerachy.Count() + 1).ToString()));
                    if (i == j)
                    {
                        ((App)App.Current)._ImagesHirerachy.Add(new ImageModel(maxProperties * 4 + i * (maxSubItems / 2) + j, i, maxProperties*2 + i * (maxSubItems / 2) + j,  "---" + (((App)App.Current)._ImagesHirerachy.Count() + 1).ToString()));
                    }

                }
            }
        }
    }
}

如果运行它,ProgressBar将不会顺利地填充:(但不会。

我在过程中使用了简单的数字(1000/100)来创建测试结构,但实际情况要高得多。

在最终的应用程序中,我将使用MVVM灯光模型,显然,将从DB / File中读取并保存数据。

请注意,两个模型都是扁平的。由于图像类中的“父”字段而给出了层次结构,如果<> 0,则表示父图像。如果= = 0,则必须将图像附加到属性。

我在这里关注的是如何在正确的层次结构中创建ViewModel结构,以便可以将Page绑定到

_PropVM

并且正在构建整个树。

c# uwp mvvm-light
1个回答
0
投票

ProgressBar无法响应,因为循环和UI在同一线程上执行。循环繁忙时,它将阻塞线程,并且无法更新UI。因此,您可以调用Task.Run( )方法来创建任务并将耗时的操作放入其中,然后使用等待执行异步操作,如下所示:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    ProgB.Value = 0;
    ProgB.Maximum = ((App)App.Current)._Properties.Count() + 1;
    foreach (PropertyModel aProperty in ((App)App.Current)._Properties)
    {
        await Task.Run(() => _PropVM.Add(new PropertyViewModel(aProperty)));
        ProgB.Value++;
    }
    ProgB.Value = ProgB.Maximum;
}
© www.soinside.com 2019 - 2024. All rights reserved.