Skip to content

Commit 84205af

Browse files
committed
Enhance async handling and UI updates across components
- Updated `Bootstrapper.cs` to call `Initialize()` after setting up the container and improved root view display with async handling. - Simplified `ConductorView.axaml` by changing `x:Name` to `Name`, removing nested elements, and introducing a new `Panel` for better data binding. - Added asynchronous activation methods in `ConductorViewModel.cs` for improved lifecycle management while retaining tab addition functionality. - Modified `ShowFeature` in `MenuViewModel.cs` for asynchronous navigation to feature view models. - Introduced `ScreenExtenstionsTests.cs` to validate activation and deactivation of screens and conductors. - Updated event handlers in `NavigationFrame.cs` to async methods for better responsiveness during navigation events.
1 parent 51cb9f6 commit 84205af

File tree

7 files changed

+207
-46
lines changed

7 files changed

+207
-46
lines changed

samples/features/Features.Avalonia/Bootstrapper.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Threading.Tasks;
4-
using System.Windows;
54
using Caliburn.Micro;
65
using Features.CrossPlatform.ViewModels;
76

@@ -16,11 +15,12 @@ public Bootstrapper()
1615
{
1716
LogManager.GetLog = type => new DebugLog(type);
1817
_container = new SimpleContainer();
19-
_container.Instance(_container);
20-
18+
_container.Instance(_container);
19+
2120
Initialize();
2221

23-
(DisplayRootViewFor<ShellViewModel>()).ConfigureAwait(false);
22+
Task displayTask = DisplayRootViewFor<ShellViewModel>();
23+
displayTask.Wait();
2424
}
2525

2626
protected override void Configure()

samples/features/Features.Avalonia/Views/ConductorView.axaml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,29 @@
1818
</StackPanel>
1919

2020

21-
<ListBox x:Name="Items" Grid.Row="1" Margin="40,0">
21+
<ListBox Name="Items" Grid.Row="1" Margin="40,0">
2222
<ListBox.ItemsPanel>
2323
<ItemsPanelTemplate>
2424
<VirtualizingStackPanel Orientation="Horizontal"/>
2525
</ItemsPanelTemplate>
2626
</ListBox.ItemsPanel>
2727
<ListBox.ItemTemplate>
2828
<DataTemplate>
29-
<StackPanel>
30-
<Border>
31-
<TextBlock Text="{Binding DisplayName}" Width="150"></TextBlock>
32-
</Border>
33-
</StackPanel>
29+
<TextBlock Text="{Binding DisplayName}"></TextBlock>
3430
</DataTemplate>
3531
</ListBox.ItemTemplate>
3632
</ListBox>
3733

38-
<ContentControl cal:View.Model="{Binding ActiveItem}" Grid.Row="2" Margin="40,20"/>
34+
<Panel DataContext="{Binding ActiveItem}" Grid.Row="2" Margin="40">
35+
<TextBlock Text="{Binding DisplayName}" FontSize="24" Foreground="Blue"/>
36+
<ItemsControl ItemsSource="{Binding Messages}" Margin="30">
37+
<ItemsControl.ItemTemplate>
38+
<DataTemplate>
39+
<TextBlock Text="{Binding}"/>
40+
</DataTemplate>
41+
</ItemsControl.ItemTemplate>
42+
</ItemsControl>
43+
</Panel>
3944

4045
</Grid>
4146
</UserControl>

samples/features/Features.CrossPlatform.Shared/ViewModels/ConductorViewModel.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Threading;
1+
using System.Threading;
32
using System.Threading.Tasks;
43
using Caliburn.Micro;
54

@@ -14,6 +13,20 @@ public ConductorViewModel()
1413
Items.CollectionChanged += (s, e) => NotifyOfPropertyChange(() => CanCloseTab);
1514
}
1615

16+
protected override async Task OnActivateAsync(CancellationToken cancellationToken)
17+
{
18+
// Optionally perform any logic before activation here
19+
await base.OnActivateAsync(cancellationToken);
20+
}
21+
protected override async Task OnActivatedAsync(CancellationToken cancellationToken)
22+
{
23+
// Optionally perform any logic after activation here
24+
await AddTabAsync();
25+
await AddTabAsync();
26+
27+
await base.OnActivatedAsync(cancellationToken);
28+
}
29+
1730
protected override async Task OnInitializedAsync(CancellationToken cancellationToken)
1831
{
1932
await AddTabAsync();
@@ -25,7 +38,7 @@ public Task AddTabAsync()
2538
return ActivateItemAsync(new TabViewModel { DisplayName = $"Tab {count++}" }, CancellationToken.None);
2639
}
2740

28-
public bool CanCloseTab => Items.Count > 1;
41+
public bool CanCloseTab => Items.Count > 1;
2942

3043
public Task CloseTab()
3144
{

samples/features/Features.CrossPlatform.Shared/ViewModels/MenuViewModel.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using Caliburn.Micro;
1+
using Caliburn.Micro;
32

43
#if XAMARINFORMS
54
using Caliburn.Micro.Xamarin.Forms;
@@ -38,10 +37,10 @@ public MenuViewModel(INavigationService navigationService, IEventAggregator even
3837

3938
public BindableCollection<FeatureViewModel> Features { get; }
4039
#if AVALONIA
41-
public async void ShowFeature(FeatureViewModel feature)
42-
{
43-
await _eventAggregator.PublishOnUIThreadAsync(feature);
44-
}
40+
public async void ShowFeature(FeatureViewModel feature)
41+
{
42+
await _navigationService.NavigateToViewModelAsync(feature.ViewModel);
43+
}
4544
#else
4645
public void ShowFeature(FeatureViewModel feature)
4746
{
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
namespace Caliburn.Micro.Avalonia.Tests
2+
{
3+
4+
5+
public class ConductWithTests
6+
{
7+
[Fact]
8+
public async Task Screen_ConductWithTests()
9+
{
10+
var root = new Screen();
11+
12+
var child1 = new StateScreen
13+
{
14+
DisplayName = "screen1"
15+
};
16+
// simulate a long deactivation process
17+
var child2 = new StateScreen(TimeSpan.FromSeconds(3))
18+
{
19+
DisplayName = "screen2"
20+
};
21+
22+
var child3 = new StateScreen()
23+
{
24+
DisplayName = "screen3"
25+
};
26+
27+
child1.ConductWith(root);
28+
child2.ConductWith(root);
29+
child3.ConductWith(root);
30+
31+
await ScreenExtensions.TryActivateAsync(root);
32+
33+
Assert.True(child1.WasActivated, "child 1 should be active");
34+
Assert.True(child2.WasActivated, "child 2 should be active");
35+
Assert.True(child3.WasActivated, "child 3 should be active");
36+
37+
await ScreenExtensions.TryDeactivateAsync(root, true);
38+
39+
Assert.True(child1.IsClosed, "child 1 should be closed");
40+
Assert.True(child2.IsClosed, "child 2 should be closed");
41+
Assert.True(child3.IsClosed, "child 3 should be closed");
42+
}
43+
44+
[Fact]
45+
public async Task Conductor_ConductWithTests()
46+
{
47+
var root = new Conductor<StateScreen>.Collection.AllActive();
48+
49+
var child1 = new StateScreen
50+
{
51+
DisplayName = "screen1"
52+
};
53+
var child2 = new StateScreen(TimeSpan.FromSeconds(3))
54+
{
55+
DisplayName = "screen2",
56+
IsClosable = false,
57+
};
58+
59+
var child3 = new StateScreen()
60+
{
61+
DisplayName = "screen3"
62+
};
63+
64+
root.Items.Add(child1);
65+
root.Items.Add(child2);
66+
root.Items.Add(child3);
67+
68+
await ScreenExtensions.TryActivateAsync(root);
69+
70+
Assert.True(child1.WasActivated, "child 1 should be active");
71+
Assert.True(child2.WasActivated, "child 2 should be active");
72+
Assert.True(child3.WasActivated, "child 3 should be active");
73+
74+
await ScreenExtensions.TryDeactivateAsync(root, true);
75+
76+
Assert.True(child1.IsClosed, "child 1 should be closed");
77+
Assert.True(child2.IsClosed, "child 2 should be closed");
78+
Assert.True(child3.IsClosed, "child 3 should be closed");
79+
}
80+
81+
class StateScreen : Screen
82+
{
83+
public StateScreen()
84+
{
85+
}
86+
87+
public StateScreen(TimeSpan? deactivationDelay)
88+
{
89+
this.deactivationDelay = deactivationDelay;
90+
}
91+
92+
public bool WasActivated { get; private set; }
93+
public bool IsClosed { get; private set; }
94+
public bool IsClosable { get; set; }
95+
96+
public override Task<bool> CanCloseAsync(CancellationToken cancellationToken = default)
97+
{
98+
return Task.FromResult(IsClosable);
99+
}
100+
101+
[Obsolete("Use OnActivateAsync instead.")]
102+
protected override async Task OnActivateAsync(CancellationToken cancellationToken)
103+
{
104+
if (deactivationDelay.HasValue)
105+
{
106+
await Task.Delay(deactivationDelay.Value, cancellationToken).ConfigureAwait(false);
107+
}
108+
109+
await base.OnActivateAsync(cancellationToken);
110+
111+
112+
WasActivated = true;
113+
IsClosable = false;
114+
}
115+
116+
protected override async Task OnActivatedAsync(CancellationToken cancellationToken)
117+
{
118+
//here
119+
if (deactivationDelay.HasValue)
120+
{
121+
await Task.Delay(deactivationDelay.Value, cancellationToken).ConfigureAwait(false);
122+
}
123+
124+
await base.OnActivatedAsync(cancellationToken);
125+
126+
WasActivated = true;
127+
IsClosable = false;
128+
}
129+
130+
protected override async Task OnDeactivateAsync(bool close, CancellationToken cancellationToken = default(CancellationToken))
131+
{
132+
await base.OnDeactivateAsync(close, cancellationToken);
133+
134+
if (deactivationDelay.HasValue)
135+
{
136+
await Task.Delay(deactivationDelay.Value, cancellationToken);
137+
}
138+
139+
IsClosed = close;
140+
}
141+
142+
private readonly TimeSpan? deactivationDelay;
143+
}
144+
}
145+
}
146+
147+

src/Caliburn.Micro.Core.Tests/ScreenExtentionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ protected override async Task OnActivateAsync(CancellationToken cancellationToke
106106
{
107107
if (deactivationDelay.HasValue)
108108
{
109-
await Task.Delay(deactivationDelay.Value, cancellationToken).ConfigureAwait(false);
109+
await Task.Delay(deactivationDelay.Value, cancellationToken);
110110
}
111111

112112
await base.OnActivateAsync(cancellationToken);
@@ -120,7 +120,7 @@ protected override async Task OnActivatedAsync(CancellationToken cancellationTok
120120
{
121121
if (deactivationDelay.HasValue)
122122
{
123-
await Task.Delay(deactivationDelay.Value, cancellationToken).ConfigureAwait(false);
123+
await Task.Delay(deactivationDelay.Value, cancellationToken);
124124
}
125125

126126
await base.OnActivatedAsync(cancellationToken);

src/Caliburn.Micro.Platform/Platforms/netcore-avalonia/NavigationFrame.cs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,38 @@ public NavigationFrame()
4646
/// </summary>
4747
/// <param name="sender">The event sender.</param>
4848
/// <param name="e">The event data.</param>
49-
private void NavigationFrame_Loaded(object sender, RoutedEventArgs e)
49+
private async void NavigationFrame_Loaded(object sender, RoutedEventArgs e)
5050
{
5151
Log.Info("Navigation Frame loaded");
52+
await ActivateItemHelper(sender);
5253
OnNavigationServiceReady(new EventArgs());
5354
}
5455

56+
private static async Task ActivateItemHelper(object sender)
57+
{
58+
var control = sender as UserControl;
59+
if (control != null)
60+
{
61+
Log.Info("Control is not null");
62+
var viewModel = control.DataContext;
63+
if (viewModel != null && viewModel is IActivate activator)
64+
{
65+
Log.Info("Activating view model");
66+
await activator.ActivateAsync();
67+
}
68+
}
69+
}
70+
5571
/// <summary>
5672
/// Handles the Content changed event of the NavigationFrame.
5773
/// </summary>
5874
/// <param name="sender">The NavigationFrame instance.</param>
5975
/// <param name="e">The event data.</param>
60-
private void NavigationFrame_ContentChanged(NavigationFrame sender, AvaloniaPropertyChangedEventArgs e)
76+
private async void NavigationFrame_ContentChanged(NavigationFrame sender, AvaloniaPropertyChangedEventArgs e)
6177
{
6278
Log.Info("Content changed");
6379
Log.Info($"Content {Tag}");
80+
await ActivateItemHelper(e.NewValue);
6481
Tag = null;
6582
}
6683

@@ -72,17 +89,7 @@ private void NavigationFrame_ContentChanged(NavigationFrame sender, AvaloniaProp
7289
private async void NavigationFrame_LayoutUpdated(object sender, EventArgs e)
7390
{
7491
Log.Info("LayoutUpdated");
75-
var control = sender as UserControl;
76-
if (control != null)
77-
{
78-
Log.Info("Control is not null");
79-
var viewModel = control.DataContext;
80-
if (viewModel != null && viewModel is IActivate activator)
81-
{
82-
Log.Info("Activating view model");
83-
await activator.ActivateAsync();
84-
}
85-
}
92+
await ActivateItemHelper(sender);
8693
}
8794

8895
/// <summary>
@@ -93,17 +100,7 @@ private async void NavigationFrame_LayoutUpdated(object sender, EventArgs e)
93100
private async void NavigationFrame_TransitionCompleted(object sender, TransitionCompletedEventArgs e)
94101
{
95102
Log.Info("Transition completed");
96-
var control = e.To as UserControl;
97-
if (!object.ReferenceEquals(e.To, e.From))
98-
{
99-
Log.Info("Control is not null");
100-
var viewModel = control?.DataContext;
101-
if (viewModel != null && viewModel is IActivate activator)
102-
{
103-
Log.Info("Activating view model");
104-
await activator.ActivateAsync();
105-
}
106-
}
103+
await ActivateItemHelper(e.To);
107104
}
108105

109106
/// <summary>

0 commit comments

Comments
 (0)