Интерфейс XAML медленный с использованием наблюдаемой коллекции MVVM - PullRequest
0 голосов
/ 17 февраля 2020

это мой первый вопрос. Я прочитал темы, связанные с моим вопросом, но не нашел решения.

В моем MainViewModel есть 10 объектов, содержащих массив кнопок, связанных с наблюдаемой коллекцией. Я хочу переключаться между объектами, чтобы показать текущий объект. Обновление пользовательского интерфейса происходит медленно. Я не уверен, что я могу попробовать.

РЕДАКТИРОВАТЬ: Я пытался создать
Минимальный, воспроизводимый пример: Имя проекта: STACK_MVVM

Mainpage.xaml

<Page
x:Class="STACK_MVVM.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:STACK_MVVM"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid x:Name="thegrid" Margin="0,0,0,0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="6*"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="0.5*"/>
        <RowDefinition Height="5*"/>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="2*"/>
    </Grid.RowDefinitions>
    <controls:UniformGrid x:Name="ButtonsUniformGrid"  Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="1"  Orientation="Horizontal"  Columns="16" Rows="5"  ColumnSpacing="4" RowSpacing="4">
    </controls:UniformGrid>
    <controls:UniformGrid x:Name="ButtonsUniformGrid_Copy"  Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="1"  Orientation="Horizontal"  Columns="10" Rows="3"  ColumnSpacing="4" RowSpacing="4" Margin="0,15,0,0">

    </controls:UniformGrid>
</Grid>
</Page>

MainPage.xaml.cs

using System;
using System.Collections.Generic;
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;



namespace STACK_MVVM
{

public sealed partial class MainPage : Page
{
    public Room[] room = new Room[10];
    public ToggleButton[] channelSel = new ToggleButton[10];
    Binding[] myChanSel_Binding_Command = new Binding[10];
    public MainViewModel TheMainViewModel1 { get; set; }
    int tabentry = 0;
    public MyLogic TheLogic = null;
    public MainPage()
    {
        this.InitializeComponent();
        this.TheMainViewModel1 = new MainViewModel();
        this.TheMainViewModel1.fillItems();
         TheLogic = new MyLogic();
         TheLogic.setTheMainModel(TheMainViewModel1);


        ButtonsUniformGrid.Visibility = Visibility.Visible;
        ButtonsUniformGrid_Copy.Orientation = Orientation.Horizontal;
        ButtonsUniformGrid_Copy.Columns = 16;
        ButtonsUniformGrid_Copy.Rows = 4;
        for (int i = 0; i < 10; i++)
        {
            room[i] = new Room();
            room[i].channel = i;
            TheLogic.setTheModels(room[i].TheMainViewModel1, i);
            thegrid.Children.Add(room[i].uniformGrid1);

            Grid.SetColumn(room[i].uniformGrid1, 1);     //ToggleButtonMatrix
            Grid.SetRow(room[i].uniformGrid1, 1);

            //****
            channelSel[i] = new ToggleButton();
            channelSel[i].HorizontalAlignment = HorizontalAlignment.Stretch;
            channelSel[i].VerticalAlignment = VerticalAlignment.Stretch;
            channelSel[i].Checked += HandleChannelSelChecked;
            channelSel[i].Tag = i;

            channelSel[i].SetBinding(ToggleButton.CommandProperty, new Binding() { Source = TheMainViewModel1, Path = new PropertyPath("OKButtonClicked1") });
            channelSel[i].SetBinding(ToggleButton.CommandParameterProperty, new Binding() { Source = TheMainViewModel1, Path = new PropertyPath("MyChannel[" + i + "]") });

            ButtonsUniformGrid_Copy.Children.Add(channelSel[i]);
        }

    }
    private void HandleChannelSelChecked(object sender, RoutedEventArgs e)   // make it Visible
    {
        ToggleButton toggle = sender as ToggleButton;
        int m = (int)toggle.Tag;
        for (int i = 0; i < 10; i++)
        {
            if (i != m)
            {
                room[i].uniformGrid1.Visibility = Visibility.Collapsed;
                channelSel[i].IsChecked = false;
            }
        }
        room[m].uniformGrid1.Visibility = Visibility.Visible;
    }
}
}

Room.cs

using Microsoft.Toolkit.Uwp.UI.Controls;
using System;
using System.Collections.Generic;
using System.Linq;

using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;

namespace STACK_MVVM
{
    public class Room
    {
        public UniformGrid uniformGrid1 = new UniformGrid();
        public Dictionary<ToggleButton, Tuple<int, int>> clientDict = new Dictionary<ToggleButton, Tuple<int, int>>();
        public ToggleButton[,] bu = new ToggleButton[5, 16];
        public int channel;

        public struct pattern
        {
            public int[,] vec_bs1;
            public int[] vec_bs;
        };
        public pattern thepattern = new pattern();
        public MainViewModel TheMainViewModel1 { get; set; }
        Binding[] Toggle_Binding = new Windows.UI.Xaml.Data.Binding[5 * 16];

    public Room()
    {
        TheMainViewModel1 = new MainViewModel();
        TheMainViewModel1.fillItems();
        thepattern.vec_bs1 = new int[5, 16];
        thepattern.vec_bs = new int[80 * 10];

        uniformGrid1.Columns = 16;
        uniformGrid1.Rows = 5;
        uniformGrid1.ColumnSpacing = 4;
        uniformGrid1.RowSpacing = 4;
        uniformGrid1.Orientation = Orientation.Horizontal;
        uniformGrid1.Visibility = Visibility.Collapsed;

        for (int i = 0; i < 5; i++)
        {
            for (int j = 0; j < 16; j++)
            {
                bu[i, j] = new ToggleButton();

                clientDict.Add(bu[i, j], new Tuple<int, int>(i, j));
                bu[i, j].HorizontalAlignment = HorizontalAlignment.Stretch;
                bu[i, j].VerticalAlignment = VerticalAlignment.Stretch;                
                uniformGrid1.Children.Add(bu[i, j]);
                //BINDINGS
                Toggle_Binding[(j) + (i * 16)] = new Windows.UI.Xaml.Data.Binding();
                Toggle_Binding[(j) + (i * 16)].Source = this.TheMainViewModel1;
                string ppath = "MyItemsbool[" + ((j) + (i * 16)) + "]";
                Toggle_Binding[(j) + (i * 16)].Path = new PropertyPath(ppath);
                Toggle_Binding[(j) + (i * 16)].Mode = BindingMode.TwoWay;
                Toggle_Binding[(j) + (i * 16)].UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                BindingOperations.SetBinding(bu[i, j], ToggleButton.IsCheckedProperty, Toggle_Binding[(j) + (i * 16)]);

            }
        }
    }
    private void HandleToggleButtonUnChecked(object sender, RoutedEventArgs e)
    {
       ToggleButton toggle = sender as ToggleButton;
       var client = clientDict[sender as ToggleButton];
       // Debug.WriteLine(client.Item1 + " " + client.Item2);
        thepattern.vec_bs1[client.Item1, client.Item2] = 0;
    }
    public void HandleToggleButtonChecked(object sender, RoutedEventArgs e)
    {
        ToggleButton toggle = sender as ToggleButton;
        var client = clientDict[sender as ToggleButton];
       // Debug.WriteLine(client.Item1 + " " + client.Item2);
        this.thepattern.vec_bs1[client.Item1, client.Item2] = 1;
    }
}//Class
}

MyLogi c .cs

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

namespace STACK_MVVM
{   public class MyLogic
   {
        public static MainViewModel[] TheModels = new MainViewModel[10];
        public MainViewModel TheMainModel = new MainViewModel();

        public void setTheModels(MainViewModel themodel, int num)
        {
            TheModels[num] = themodel;
            //Debug.WriteLine("THEMODELS" + TheModels[0].MyItemsbool[0]);
        }
        public void setTheMainModel(MainViewModel themainmodel)
        {
            TheMainModel = themainmodel;
        }
        public static void LoadPattern(object parameter)
        {
           for (int x = 0; x < 10; x++)
           {
                TheModels[x].pattern_load_struct((int)parameter);
           }
            Debug.Write("CHANNELNUM: " + parameter);
        }
   }
}

MainViewModelBase.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;

namespace STACK_MVVM
{
    public abstract class MainViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private CoreDispatcher _dispatcher = CoreApplication.MainView.Dispatcher;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (_dispatcher.HasThreadAccess)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
            else
            {
                _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                });
            }
        }
    }
}

MainViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace STACK_MVVM
{
    public class MainViewModel : MainViewModelBase
    {
        public struct pattern
        {   
            public int[] vec_bs1;
            public int[] vec_bs;
        };
        public pattern thepattern = new pattern();

        private ObservableCollection<int> _myChannel = new ObservableCollection<int>(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
        private ObservableCollection<bool> _myItemsbool = new ObservableCollection<bool>(new[] { true, false, true });    
        public MainViewModel()
        {
            thepattern.vec_bs1 = new int[5 * 16];
            thepattern.vec_bs = new int[80 * 10];
        }
        public ObservableCollection<bool> MyItemsbool
        {
            get { return _myItemsbool; }
            set
            {
                _myItemsbool = value;
            }
        }
        public ObservableCollection<int> MyChannel { get => _myChannel; set => _myChannel = value; }
        public void fillItems()
        {

            for (int i = 0; i < 5; i++)
                for (int j = 0; j < 16; j++)
                {
                    {

                        MyItemsbool.Add(true);
                        MyItemsbool[(j) + (i * 16)] = true;
                        //MyItemsbool[(j) + (i * 16)] = rnd.Next(2) !=0;
                    }
                }
        }
        public ICommand OKButtonClicked1
        {
            get
            {
               return new DelegateCommand1<object>(MyLogic.LoadPattern);
            }
        }

       public void pattern_save_struct(int tabentry)
       {
            for (int i = 0; i < 5; i++)
            {
                for (int j = 0; j < 16; j++)
                {
                    thepattern.vec_bs[(j) + (i * 16) + ((80) * tabentry)] = (MyItemsbool[(j) + (i * 16)]) ? 1 : 0;
                    //  thepattern.vec_bs[(j) + (i * 16) + ((80) * tabentry)] = thepattern.vec_bs1[i, j];   
                    // thepattern.vec_bs[(j) + (i * 16) + ((80) * tabentry)] = thepattern.vec_bs1[i, j];   
                }
            }
       }
        public async Task pattern_load_struct(int tabentry)
        {
            var rnd = new Random();  //this is just for testing - Randomly Activate Cell

                await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                Array.Copy(thepattern.vec_bs, 80 * tabentry, thepattern.vec_bs1, 0, 80);
                // Array.Copy(thepattern.vec_bs1, 0, MyItemsbool, 0, 80); //DONT WORK

                for (int i = 0; i < 80; i++)
                {
                     thepattern.vec_bs1[i] = rnd.Next(2);            // Randomly activate Cell - this line can be deletet.

                    MyItemsbool[i] = thepattern.vec_bs1[i] != 0;
                }
            });
        }
    }
}

DelegateCommand1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace STACK_MVVM
{
    class DelegateCommand1<T> : ICommand
    {
        private readonly Action<T> handler;
        private bool isEnabled = true;
        public event EventHandler CanExecuteChanged;
        public delegate void SimpleEventHandler();
        public DelegateCommand1(Action<T> handler)
        {
            this.handler = handler;
        }
        public bool IsEnabled
        {
            get
            {
                return this.isEnabled;
            }
        }
        void ICommand.Execute(object org)
        {
            this.handler((T)org);
        }
        bool ICommand.CanExecute(object org)
        {
            return this.IsEnabled;
        }
        private void OnCanExecuteChanged()
        {
            if (this.CanExecuteChanged != null)
            {
                this.CanExecuteChanged(this, EventArgs.Empty);
            }
        }
    }
}

1 Ответ

1 голос
/ 18 февраля 2020

Я посмотрел на ваш код, и вы, кажется, создали все элементы управления (включая UniformGrid и множество ToggleButton с) в коде C# и добавили их в пользовательский интерфейс при необходимости.

Это на самом деле огромные накладные расходы для системы, и отладка неудобна.

Ваша проблема в том, что она не соответствует шаблону проектирования MVVM. В MVVM, пожалуйста, не создавайте элементы управления непосредственно в C# коде, а используйте его с классом данных (Model).

View в MVVM (модель, вид, вид-модель) может будет рассматриваться как DataTemplate в UWP. Рекомендуемый метод:

  1. Создайте класс для room и класс для apartment:
public class Room
{
    public bool IsOpened { get; set; }
    public string Name { get; set; }
}
public class Apartment
{
    public string Name { get; set; }
    public List<Room> Rooms { get; set; }
}
Создайте DataTemplate в MainPage.xaml и используйте привязку для привязки соответствующих свойств.
<Page.Resources>
    <DataTemplate x:DataType="local:Room" x:Key="RoomItemTemplate">
        <ToggleButton IsChecked="{x:Bind IsOpened}"/>
    </DataTemplate>
    <DataTemplate x:DataType="local:Apartment" x:Key="ApartmentItemTemplate">
        <ToggleButton Content="{x:Bind Name}"/>
    </DataTemplate>
</Page.Resources>
Используйте GridView для связывания коллекций.

xaml

...
<GridView ItemTemplate="{StaticResource RoomItemTemplate}"
          x:Name="RoomGridView"
          />

<GridView ItemTemplate="{StaticResource ApartmentItemTemplate}"
          x:Name="ApartmentGridView"
          IsItemClickEnabled="True"
          ItemsSource="{x:Bind ApartmentCollection}"
          ItemClick="ApartmentGridView_ItemClick"/>
...

xaml.cs

public ObservableCollection<Apartment> ApartmentCollection = new ObservableCollection<Apartment>();
//...
private void ApartmentGridView_ItemClick(object sender, ItemClickEventArgs e)
{
    var item = e.ClickedItem as Apartment;
    RoomGridView.ItemsSource = item.Rooms;
}

Следует отметить, что приведенный выше код является упрощенным кодом. Если вы хотите перенести текущий код в этот режим, вам необходимо реорганизовать ваш код.

С GridView вы можете использовать виртуализацию самого элемента управления для сокращения потребления ресурсов. В то же время этот метод DataTemplate может значительно упростить написание кода.

Что касается того, что вы сказали для изменения состояния одного элемента, ObservableCollection не имеет ответа. Это нормально, поскольку ObservableCollection реагирует только на изменение количества элементов в коллекции. Если вы хотите уведомить пользовательский интерфейс при изменении свойств класса данных, вам необходимо реализовать интерфейс INotifyPropertyChanged для модели.


Вот несколько документов, которые могут вам помочь:


Обновление

Если GridView все еще застрял, это может быть вызвано расчетом анимации по умолчанию. Для решения этой проблемы вы можете использовать ItemsControl.

<ItemsControl ItemTemplate="{StaticResource RoomItemTemplate}"
  x:Name="RoomGridView"
          Grid.Column="0" Grid.Row="0">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <controls:UniformGrid Columns="16" Rows="5" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...