Reputation: 31
this is my first question. I have read topics related to my question but didn't find a solution.
There are 10 objects holding an array of buttons bound to an observable collection in my MainViewModel. I want to switch between the objects to show the current object. Updating the ui is slow this way. I'm not sure what I can try.
EDIT: I tried to create a
Minimal, Reproducible Example:
Name od the Project: 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
}
MyLogic.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);
}
}
}
}
Upvotes: 0
Views: 911
Reputation: 7727
I looked at your code and you seem to have created all the controls (including UniformGrid
and a lots of ToggleButton
s) in the C# code and filled them into the UI when needed.
This is actually a huge overhead for the system and it is not convenient to debug.
Your problem is that it does not follow the design pattern of MVVM. In MVVM, please do not to create controls directly in C# code, but operate it with data class (Model
).
The View
in MVVM(Model, View, View-Model) can be regarded as a DataTemplate
in UWP. The recommended method is as follows:
room
and a class for 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
in MainPage.xaml
and use the binding to bind the corresponding properties.<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
to bind collections.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;
}
It should be noted that the above code is simplified code. If you want to migrate the current code to this mode, you need to reorganize your code.
With GridView, you can use the virtualization of the control itself to reduce resource consumption. At the same time, this DataTemplate method can greatly simplify the code writing.
Regarding what you said to change the state of a single item, ObservableCollection
has no response. This is normal, because ObservableCollection
only responds to changes in the number of items in the collection. If you want to notify the UI when changing the properties of the data class, you need to implement the INotifyPropertyChanged
interface for the Model.
Here are some documents that might help you:
Update
If the GridView
is still stuck, this may be caused by its default animation calculation. You can use ItemsControl
to solve this problem.
<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>
Upvotes: 1