I have a calendar view that is slow in uploading. The part that slows down the calendarview should be in the CalendarPageViewModel.cs. But I do not know how to improve it because it's a loop for.
Whole project Calendar View.
CalendarPicker.xaml:
<UserControl
x:Class="CalendarView.CalendarPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CalendarView"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Loaded="Page_Loaded"
Height="500"
Width="350">
<UserControl.DataContext>
<local:CalendarPageViewModel x:Name="ViewModel"/>
</UserControl.DataContext>
<Grid>
<Grid Background="#FF106F97">
<Grid Width="350" Height="500" HorizontalAlignment="Center" VerticalAlignment="Bottom">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button x:Name="btnToday" Click="btnToday_Click" FontSize="20" Foreground="White" VerticalAlignment="Stretch" Style="{StaticResource ButtonStyleToday}" Padding="4" HorizontalAlignment="Stretch"/>
<TextBlock x:Name="txtSelectedDay" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="12,0,0,0" FontSize="20" Foreground="White"/>
<Button x:Name="btnMonthYear" Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="25" Foreground="White" Margin="12,0,0,0" Style="{StaticResource ButtonStyleTrasparent}" Padding="0,4,8,4"/>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Height="50" VerticalAlignment="Center">
<Button x:Name="btnDecYearMonthDay" Click="btnDecYearMonthDay_Click" Content="" FontFamily="Segoe MDL2 Assets" VerticalAlignment="Stretch" Width="50" FontSize="30" Padding="0" BorderBrush="White" Foreground="White" BorderThickness="0" Style="{StaticResource ButtonStyleTrasparent}"/>
<Button x:Name="btnIncYearMonthDay" Click="btnIncYearMonthDay_Click" Content="" FontFamily="Segoe MDL2 Assets" VerticalAlignment="Stretch" Width="50" FontSize="30" Padding="0" BorderBrush="White" Foreground="White" BorderThickness="0" Style="{StaticResource ButtonStyleTrasparent}"/>
</StackPanel>
<Grid x:Name="BaseGridDays" Visibility="Visible" Grid.Row="3" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<CompositeTransform/>
</Grid.RenderTransform>
<Grid x:Name="GridHeadingDay" Height="50" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="lu" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/>
<TextBlock Grid.Column="1" Text="ma" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/>
<TextBlock Grid.Column="2" Text="me" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/>
<TextBlock Grid.Column="3" Text="gi" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/>
<TextBlock Grid.Column="4" Text="ve" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/>
<TextBlock Grid.Column="5" Text="sa" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/>
<TextBlock Grid.Column="6" Text="do" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/>
</Grid>
<Canvas x:Name="CanvasRootDays" Height="300" VerticalAlignment="Bottom">
<GridView x:Name="GridDays" ItemsSource="{Binding ItemsDay}" IsItemClickEnabled="True" ItemClick="GridDays_ItemClick" Height="300" Width="350" Padding="0" IsSwipeEnabled="False" IsTabStop="False" IsSynchronizedWithCurrentItem="False" ItemContainerStyle="{StaticResource GridViewItemStyle}" Style="{StaticResource GridViewStyleAnniMesiGiorni}" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:CalendarDay">
<Grid Width="50" Height="50" Background="{x:Bind ToDayColor}">
<TextBlock Text="{x:Bind ProgressiveDay, Mode=OneWay}" Foreground="{x:Bind daysColor, Mode=OneWay}" FontSize="15" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Canvas>
</Grid>
</Grid>
</Grid>
</Grid>
CalendarPicker.xaml.cs:
public sealed partial class CalendarPicker : UserControl
{
DispatcherTimer timerVisDays = new DispatcherTimer();
DispatcherTimer timerTimeNow = new DispatcherTimer();
int FirstDay;
public CalendarPicker()
{
InitializeComponent();
timerVisDays.Interval = new TimeSpan(0, 0, 0, 0, 30);
timerVisDays.Tick += timerShowDays_tick;
timerVisDays.Start();
}
private void GridDays_ItemClick(object sender, ItemClickEventArgs e)
{
var item = e.ClickedItem as CalendarDay;
var index = (ViewModel.ItemsDay).IndexOf(item);
string dayString = FindStringWeek(Convert.ToUInt16(ViewModel.listDateDay[index - 1].DayOfWeek));
int day = ViewModel.listDateDay[index].Day;
string month = FindStringMonth(ViewModel.listDateDay[index].Month);
int year = ViewModel.listDateDay[index].Year;
txtSelectedDay.Text = dayString + " " + day.ToString() + " " + month + " " + year.ToString();
}
private void timerShowDays_tick(object sender, object e)
{
ScrollViewer listScrollViewerDays = GetScrollViewer(GridDays);
FirstDay = (Convert.ToUInt16(Math.Truncate(listScrollViewerDays.VerticalOffset / 50)) * 7);
int numberOfDates;
double remnant = (listScrollViewerDays.VerticalOffset / 50) - (Math.Truncate(listScrollViewerDays.VerticalOffset / 50));
if (remnant == 0)
{
numberOfDates = 42;
}
else
{
numberOfDates = 49;
}
int nDaysMonthFirstDate = FindNumberOfDays(ViewModel.listDateDay[FirstDay].Month);
int firstDayPresent = ViewModel.listDateDay[FirstDay].Day;
int nDaysPresentFirstDate = nDaysMonthFirstDate - (firstDayPresent - 1);
int nDaysPresentSecondDate = numberOfDates - nDaysPresentFirstDate;
if (nDaysPresentFirstDate > nDaysPresentSecondDate)
{
for (int a = FirstDay; a <= FirstDay + (numberOfDates - 1); a++)
{
if (a <= ViewModel.ItemsDay.Count - 1)
{
if (ViewModel.listDateDay[a].Month != ViewModel.listDateDay[FirstDay].Month)
{
ViewModel.ItemsDay[a].daysColor = new SolidColorBrush(Color.FromArgb(255, 160, 160, 160));
}
}
}
}
else
{
for (int a = FirstDay; a <= FirstDay + (numberOfDates - 1); a++)
{
if (a <= ViewModel.ItemsDay.Count - 1)
{
if (ViewModel.listDateDay[a].Month != ViewModel.listDateDay[FirstDay].AddMonths(1).Month)
{
ViewModel.ItemsDay[a].daysColor = new SolidColorBrush(Color.FromArgb(255, 160, 160, 160));
}
}
}
}
timerVisDays.Stop();
}
private string FindStringWeek(int gs)
{
if (true == (gs == 0))
{
return "monday";
}
else if (true == (gs == 1))
{
return "tuesday";
}
else if (true == (gs == 2))
{
return "wednesday";
}
else if (true == (gs == 3))
{
return "thursday";
}
else if (true == (gs == 4))
{
return "friday";
}
else if (true == (gs == 5))
{
return "saturday";
}
else if (true == (gs == 6))
{
return "sunday";
}
else
{
return "";
}
}
private string FindStringMonth(int gs)
{
if (true == (gs == 1))
{
return "ganuary";
}
else if (true == (gs == 2))
{
return "february";
}
else if (true == (gs == 3))
{
return "march";
}
else if (true == (gs == 4))
{
return "april";
}
else if (true == (gs == 5))
{
return "may";
}
else if (true == (gs == 6))
{
return "june";
}
else if (true == (gs == 7))
{
return "july";
}
else if (true == (gs == 8))
{
return "august";
}
else if (true == (gs == 9))
{
return "september";
}
else if (true == (gs == 10))
{
return "october";
}
else if (true == (gs == 11))
{
return "november";
}
else if (true == (gs == 12))
{
return "december";
}
else
{
return "";
}
}
private int FindNumberOfDays(int gs)
{
if (true == (gs == 1))
{
return 31;
}
else if (true == (gs == 2))
{
return 29;
}
else if (true == (gs == 3))
{
return 31;
}
else if (true == (gs == 4))
{
return 30;
}
else if (true == (gs == 5))
{
return 31;
}
else if (true == (gs == 6))
{
return 30;
}
else if (true == (gs == 7))
{
return 31;
}
else if (true == (gs == 8))
{
return 31;
}
else if (true == (gs == 9))
{
return 30;
}
else if (true == (gs == 10))
{
return 31;
}
else if (true == (gs == 11))
{
return 30;
}
else if (true == (gs == 12))
{
return 31;
}
else
{
return 0;
}
}
public ScrollViewer GetScrollViewer(DependencyObject o)
{
if (o is ScrollViewer)
{
return o as ScrollViewer;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
{
var child = VisualTreeHelper.GetChild(o, i);
var result = GetScrollViewer(child);
if (result == null)
{
continue;
}
else
{
return result;
}
}
return null;
}
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
ScrollViewer listScrollViewerDays = GetScrollViewer(GridDays);
GridDays.SelectedItem = GridDays.Items[ViewModel.indexDDay];
ViewModel.ItemsDay[ViewModel.indexDDay].ToDayColor = new SolidColorBrush(Color.FromArgb(255, 17, 95, 155));
await Task.Delay(100);
GridDays.ScrollIntoView(GridDays.Items[ViewModel.indexToDay], ScrollIntoViewAlignment.Leading);
listScrollViewerDays.ViewChanged += DisplayDays;
btnToday.Content = FindStringWeek(Convert.ToInt32(DateTime.Now.DayOfWeek)) + " " + DateTime.Now.Day.ToString() + " " + FindStringMonth(DateTime.Now.Month) + " " + DateTime.Now.Year.ToString();
}
private void DisplayDays(object sender, ScrollViewerViewChangedEventArgs e)
{
timerVisDays.Stop();
timerVisDays.Start();
ScrollViewer listScrollViewer = GetScrollViewer(GridDays);
FirstDay = (Convert.ToUInt16(Math.Truncate(listScrollViewer.VerticalOffset / 50)) * 7);
int numberOfDates;
string currentMonth = "";
string currentYear = "";
int indexCorrentMonth;
double remnant = (listScrollViewer.VerticalOffset / 50) - (Math.Truncate(listScrollViewer.VerticalOffset / 50));
if (remnant == 0)
{
numberOfDates = 42;
}
else
{
numberOfDates = 49;
}
int nDaysMonthFirstDate = FindNumberOfDays(ViewModel.listDateDay[FirstDay].Month);
int firstDayPresent = ViewModel.listDateDay[FirstDay].Day;
int nDaysPresentFirstDate = nDaysMonthFirstDate - (firstDayPresent - 1);
int nDaysPresentSecondDate = numberOfDates - (nDaysPresentFirstDate);
if (nDaysPresentFirstDate > nDaysPresentSecondDate)
{
currentMonth = FindStringMonth(ViewModel.listDateDay[FirstDay].Month);
currentYear = ViewModel.listDateDay[FirstDay].Year.ToString();
indexCorrentMonth = FirstDay;
}
else
{
int numMonth = ViewModel.listDateDay[FirstDay].AddMonths(1).Month;
currentYear = ViewModel.listDateDay[FirstDay].AddMonths(1).Year.ToString();
currentMonth = FindStringMonth(numMonth);
}
btnMonthYear.Content = currentMonth + " " + currentYear;
for (int a = FirstDay; a <= FirstDay + numberOfDates; a++)
{
try
{
ViewModel.ItemsDay[a].daysColor = new SolidColorBrush(Colors.White);
}
catch { }
}
}
private async void IncreasesMonth()
{
ScrollViewer listScrollViewer = GetScrollViewer(GridDays);
FirstDay = (Convert.ToUInt16(Math.Truncate(listScrollViewer.VerticalOffset / 50)) * 7);
int numberOfDates;
bool firstdayfound = false;
DateTime indexSucceedingMonth;
double remnant = (listScrollViewer.VerticalOffset / 50) - (Math.Truncate(listScrollViewer.VerticalOffset / 50));
if (remnant == 0)
{
numberOfDates = 42;
}
else
{
numberOfDates = 49;
}
int nDaysMonthFirstDate = FindNumberOfDays(ViewModel.listDateDay[FirstDay].Month);
int firstDayPresent = ViewModel.listDateDay[FirstDay].Day;
int nDaysPresentFirstDate = nDaysMonthFirstDate - (firstDayPresent - 1);
int nDaysPresentSecondDate = numberOfDates - (nDaysPresentFirstDate);
if (nDaysPresentFirstDate > nDaysPresentSecondDate)
{
indexSucceedingMonth = new DateTime(ViewModel.listDateDay[FirstDay].Year, ViewModel.listDateDay[FirstDay].AddMonths(1).Month, 1, 0, 0, 0);
while (firstdayfound == false)
{
if (ViewModel.listDateDay[FirstDay] == indexSucceedingMonth)
{
firstdayfound = true;
}
FirstDay += 1;
}
}
else
{
DateTime IncreasedDate = ViewModel.listDateDay[FirstDay].AddMonths(2);
indexSucceedingMonth = new DateTime(IncreasedDate.Year, IncreasedDate.Month, 1, 0, 0, 0);
while (firstdayfound == false)
{
if (ViewModel.listDateDay[FirstDay] == indexSucceedingMonth)
{
firstdayfound = true;
}
FirstDay += 1;
}
}
var item = GridDays.ContainerFromIndex(FirstDay - 1) as GridViewItem;
var gt = item.TransformToVisual(CanvasRootDays);
double offestCanvas = gt.TransformPoint(new Point(0, 0)).Y;
listScrollViewer.ChangeView(null, listScrollViewer.VerticalOffset + offestCanvas, null, false);
await Task.Delay(300);
}
private async void DecreaseMonth()
{
ScrollViewer listScrollViewer = GetScrollViewer(GridDays);
FirstDay = (Convert.ToUInt16(Math.Truncate(listScrollViewer.VerticalOffset / 50)) * 7);
int numberOfDates;
bool firstdayfound = false;
DateTime indexSucceedingMonth;
double avanzo = (listScrollViewer.VerticalOffset / 50) - (Math.Truncate(listScrollViewer.VerticalOffset / 50));
if (avanzo == 0)
{
numberOfDates = 42;
}
else
{
numberOfDates = 49;
}
int nDaysMonthFirstDate = FindNumberOfDays(ViewModel.listDateDay[FirstDay].Month);
int firstDayPresent = ViewModel.listDateDay[FirstDay].Day;
int nDaysPresentFirstDate = nDaysMonthFirstDate - (firstDayPresent - 1);
int nDaysPresentSecondDate = numberOfDates - (nDaysPresentFirstDate);
if (nDaysPresentFirstDate > nDaysPresentSecondDate)
{
DateTime IncreasedDate = ViewModel.listDateDay[FirstDay].AddMonths(-1);
indexSucceedingMonth = new DateTime(IncreasedDate.Year, IncreasedDate.Month, 1, 0, 0, 0);
while (firstdayfound == false)
{
if (ViewModel.listDateDay[FirstDay] == indexSucceedingMonth)
{
firstdayfound = true;
}
FirstDay -= 1;
}
}
else
{
DateTime IncreasedDate = ViewModel.listDateDay[FirstDay];
indexSucceedingMonth = new DateTime(IncreasedDate.Year, IncreasedDate.Month, 1, 0, 0, 0);
while (firstdayfound == false)
{
if (ViewModel.listDateDay[FirstDay] == indexSucceedingMonth)
{
firstdayfound = true;
}
FirstDay -= 1;
}
}
var item = GridDays.ContainerFromIndex(FirstDay + 1) as GridViewItem;
var gt = item.TransformToVisual(CanvasRootDays);
double offestCanvas = gt.TransformPoint(new Point(0, 0)).Y;
listScrollViewer.ChangeView(null, listScrollViewer.VerticalOffset - (offestCanvas * -1), null, false);
await Task.Delay(300);
}
private void btnDecYearMonthDay_Click(object sender, RoutedEventArgs e)
{
DecreaseMonth();
}
private void btnIncYearMonthDay_Click(object sender, RoutedEventArgs e)
{
IncreasesMonth();
}
private void btnToday_Click(object sender, RoutedEventArgs e)
{
GridDays.SelectedItem = GridDays.Items[ViewModel.indexDDay];
GridDays.ScrollIntoView(GridDays.Items[ViewModel.indexToDay], ScrollIntoViewAlignment.Leading);
}
}
CalendarPageViewModel.cs:
class CalendarPageViewModel : ViewModelBase
{
private ObservableCollection<CalendarDay> _itemsDay;
private ObservableCollection<DateTime> _listaDateDay = new ObservableCollection<DateTime>();
private ObservableCollection<DateTime> _listaDateMonth = new ObservableCollection<DateTime>();
private ObservableCollection<DateTime> _listaDateYear = new ObservableCollection<DateTime>();
public ObservableCollection<CalendarDay> ItemsDay
{
get
{
return _itemsDay;
}
set
{
_itemsDay = value;
OnPropertyChanged();
}
}
public ObservableCollection<DateTime> listDateDay
{
get
{
return _listaDateDay;
}
set
{
_listaDateDay = value;
OnPropertyChanged();
}
}
public ObservableCollection<DateTime> listDateMonth
{
get
{
return _listaDateMonth;
}
set
{
_listaDateMonth = value;
OnPropertyChanged();
}
}
public ObservableCollection<DateTime> listDateYear
{
get
{
return _listaDateYear;
}
set
{
_listaDateYear = value;
OnPropertyChanged();
}
}
public int indexToDay { get; set; }
public int indexDDay { get; set; }
public CalendarPageViewModel()
{
var listDay = new ObservableCollection<CalendarDay>();
DateTime monthToDay = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1, 0, 0, 0);
DateTime monthToDayM = new DateTime(DateTime.Now.Year, 1, 1, 0, 0, 0);
DateTime yearsToDay = new DateTime(DateTime.Now.Year, 1, 1, 0, 0, 0);
DateTime dayOfToday = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
DateTime progressiveDay = new DateTime(1900, 1, 1);
listDateDay.Add(progressiveDay);
listDateMonth.Add(new DateTime(progressiveDay.Year, 1, 1, 0, 0, 0));
listDateYear.Add(new DateTime(progressiveDay.Year, 1, 1, 0, 0, 0));
for (var i = 0; i < 72000; i++)
{
listDay.Add(new CalendarDay { ProgressiveDay = progressiveDay.Day.ToString() });
progressiveDay = progressiveDay.AddDays(1);
listDateDay.Add(progressiveDay);
}
for (int a = 0; a <= listDateDay.Count - 1; a++)
{
if (listDateDay[a] == monthToDay)
{
indexToDay = a;
}
else if (listDateDay[a] == dayOfToday)
{
indexDDay = a;
}
}
_itemsDay = listDay;
}
}
3 Answers 3
Efficiency
The most obvious problem is that you're adding 72.000 CalendarDay
items to a collection that your CalendarPicker
is binding to:
- It's inconvenient. The
CalendarPicker
can apparently only be used when it's supplied withCalendarDay
items. I expect such a control to be usable immediately, while perhaps providing some customization via dependency properties likeMinimumDate
andMaximumDate
. - It's going to be slow if you don't use UI virtualization.
- It's inefficient: each
CalendarDay
contains two brushes and some text. Those brushes can be reused (you only need 2, not 72.000 * 2) and that text can be generated on demand.
Other observations
- Your control is intricately linked to
CalendarPageViewModel
. It looks like it's going to be difficult to use in any other context. Custom controls normally don't know anything about view models. Instead, they expose information via dependency properties, which can be used with data binding. In this case, aSelectedDay
property would seem appropriate. - You only need observable collections when you actually expect items to be added or removed. Likewise, you only need property change notifications when you expect property values to change. None of that seems to apply to
CalendarDay
. - The code contains several 'magic numbers' and hard-coded day/month names. Magic numbers make code more difficult to understand, and hard-coded names make it difficult to localize (or otherwise customize the display of) this control.
- Instead of a large
if/else if
chain, consider using a lookup table forFindNumberOfDays
- an array will do.
The code in general strikes me as very tightly coupled, and thus difficult to reuse or customize. There's probably more that can be said about this, but I don't have a lot of time right now, so maybe later.
-
\$\begingroup\$ How can I use UI virtualization? \$\endgroup\$LightGreen– LightGreen2017年09月27日 18:35:23 +00:00Commented Sep 27, 2017 at 18:35
-
\$\begingroup\$ If you search for 'uwp UI virtualization' you'll find various articles, some on docs.microsoft.com, that describe what it is and how you can use it. It looks like UWP's
GridView
already supports it automatically. However, your grid view is wrapped in a canvas, which doesn't restrict the size of its children, so your grid view probably won't virtualize anything because it thinks that all its content is visible. \$\endgroup\$Pieter Witvoet– Pieter Witvoet2017年09月28日 08:26:27 +00:00Commented Sep 28, 2017 at 8:26
First of all, you have some duplications both in XAML and code. For example, these lines
<TextBlock Grid.Column="0" Text="lu" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/> <TextBlock Grid.Column="1" Text="ma" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/> <TextBlock Grid.Column="2" Text="me" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" FontSize="25"/> ...
should be rewritten with use of style. You can define either explicit style (with specified x:Key
) or implicit one (that will be applied automatically to all controls of the specified type):
<Style TargetType="{x:Type TextBlock}"
BasedOn="{StaticResource {x:Key TextBlock}}">
<Setter Property="Foreground"
Value="White"/>
<Setter Property="VerticalAlignment"
Value="Center"/>
...
</Style>
Then you can write just:
<TextBlock Grid.Column="0"
Text="lu"/>
Also please place each property of a control on separate line in XAML because it is very difficult to read the code where lines go far to the right.
I recommend you always use x:Type
instead of type-as-string. There are cases where things like local:CalendarDay
just will not work. One example is implicit data templates.
You must rewrite the FindStringWeek
, FindStringMonth
and FindNumberOfDays
methods. By the way, if (true == (gs == 8))
looks pretty awkward since you can write just if (gs == 8)
.
private static readonly Dictionary<int, string> _daysOfWeek = new Dictionary<int, string>
{
[0] => "monday",
[1] => "tuesday",
...
};
private string FindStringWeek(int gs)
{
return _daysOfWeek .TryGetValue(gs, out var dayOfWeek)
? dayOfWeek
: string.Empty;
}
You have a lot of manipulations with view-model in code behind. It is not good. Try to move all your business logic to the view-model.
IncreasesMonth
method has async void
signature. You should avoid such methods and use async Task
instead. It is acceptable to mark event handlers with async
. IncreasesMonth
is used only in btnIncYearMonthDay_Click
event handler, so do the following changes:
private async void btnIncYearMonthDay_Click(object sender, RoutedEventArgs e)
{
await IncreasesMonth();
}
private async Task IncreasesMonth()
{
...
}
The same is applied to the DecreaseMonth
method.
Are you sure you need setters for observable collection properties in the CalendarPageViewModel
? I believe no. So remove setters from the ItemsDay
, listDateDay
, listDateMonth
and listDateYear
properties. Starting with C# 6 you even don't need private fields:
public ObservableCollection<CalendarDay> ItemsDay { get; } = new ObservableCollection<CalendarDay>();
Also all properties in C# should have PascalCased names according to naming guidelines. So the listDateDay
is a bad name and you should use ListDateDay
as well as IndexToDay
and IndexDDay
.
View-model should never think about view if you want to write good WPF apps which means you should use MVVM correctly. Your indexToDay
and indexDDay
properties violate this rule. What are these indices? Where are they used in the view model? As I can see you use them only for updating view:
private void btnToday_Click(object sender, RoutedEventArgs e) { GridDays.SelectedItem = GridDays.Items[ViewModel.indexDDay]; GridDays.ScrollIntoView(GridDays.Items[ViewModel.indexToDay], ScrollIntoViewAlignment.Leading); }
Accessing Items
of an ItemsControl
almost always a sign of bad code. You should attach a command to your button which will execute some actions in view model. Those actions will update some properties which will raise PropertyChanged
event. It will cause controls in the view that are bound via bindings to those properties get updated. It is the correct workflow in a WPF application so you should definitely refactor your code.
The first thought that come to your mind where you need perform an action on button click should be using of Command
.
Also you use view related classes inside a view model:
ViewModel.ItemsDay[a].daysColor = new SolidColorBrush(Color.FromArgb(255, 160, 160, 160));
It is completely wrong. You should expose a property which defines kind of day state and then react on changes of the state in view via binding and converter or triggers.
So you need to read again about MVVM and take a look at examples of proper implementation of it.
First I would recommend you using debugger tools or code some stopwatches in to see exactly what is causing the slow down. As it seems your question just assumes it's the for loop. You'll want to make sure before trying to optimize that part.
If you need to optimize that part you really have two choices.
One don't load all that data right away. Just load what you need to show and then when clicking forward to next month or year then load up again just enough data to show that if it's not already loaded. You can even tie into the scrollview to know when you need to load more data You can use something like the ConcurrentDictionary GetOrAdd method and have the key be the month/year if you want to load by month/year and then load just that bucket.
Another option if you really want to load the data upfront is don't have the system wait for the data to be loaded but show the screen right away and update the data as it becomes available. This will mean you need to switch the loading onto another thread and have it report back the update on the Dispatcher. You can use Rx for this (example) or code it yourself.