Я построил этот пример, чтобы показать мою проблему. Мне нужно создать иерархию, которая будет отображаться в древовидном представлении, древовидное представление должно быть привязано к модели представления. В старые добрые времена VB6 я бы использовал DoEvents, чтобы разблокировать пользовательский интерфейс, но здесь я не могу понять, как действовать.
На 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.
Обратите внимание, чтообе модели плоские. Иерархия задается благодаря полю "parent" в классе изображений, которое, если is <> 0, относится к родительскому изображению. Если is = 0, то изображение должноприсоединиться к свойству.
Здесь я сосредоточусь на том, как создать структуры ViewModel в правильной иерархии, чтобы я мог связать страницу с
_PropVM
и получитьцелое дерево строится.