Caliburn Micro: Screen.TryClose does not work - caliburn.micro

I am running out of hair in frustration.
I have made a somewhat minimal example where the same viewmodel/view acts as main window and dialog. Hope this does not cause confusion.
class DialogViewModel : Screen
{
private readonly IWindowManager _windowManager;
public DialogViewModel(IWindowManager windowManager)
{
_windowManager = windowManager;
}
public void ShowDialog()
{
_windowManager.ShowDialog(new DialogViewModel(_windowManager));
}
//----------
public DialogViewModel()
{
}
public void Close()
{
TryClose();
//TryClose(true);
//TryClose(false);
}
}
<Window x:Class="Views.DialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro.Platform"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="200">
<WrapPanel>
<Button>
Close
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="Close" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button>
Show Dialog
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="ShowDialog" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</WrapPanel>
</Window>
public sealed class AppBootstrapper : BootstrapperBase
{
private SimpleContainer _container;
public AppBootstrapper() : base(true)
{
StartRuntime();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<DialogViewModel>();
}
protected override void Configure()
{
_container = new SimpleContainer();
_container.Singleton<IWindowManager, WindowManager>();
_container.Singleton<IEventAggregator, EventAggregator>();
_container.PerRequest<DialogViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = _container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
The call TryClose() does not initiate OnDeactivate.
According to https://caliburnmicro.codeplex.com/wikipage?title=Screens,%20Conductors%20and%20Composition and the several posts here on stackoverflow the above should be enough. But nothing happens when I call TryClose().
Please note that code does run and show the dialogs on my machine. If something is missing, please let me know.
Any ideas why?
Thank you!

StartRuntime should be Initialize(), codeplex code is really really outdated with release of v2. Reference github.com/bluespire/Caliburn.Micro or CaliburnMicro.com. I assume this is a WPF App. Also your Button's don't appear to be correct.
<Button x:Name="ShowDialog" Content="ShowDialog" />
<Button x:Name="Close" Content="Close" />
I am assuming this is a WPF application.

Related

Xamarin Forms -> Activity Indicator not working if Commands of statements to be executed

Using Visual Studio 2017 Community 15.8.1
This is after going through all options of stackoverflow regarding ActivityIndicator. So though it may be a duplication but nothing is helping me out.
So finally decided to post my workouts and get best help from here.
What I have tried till now:-
1. {Binding IsLoading} + INotifyPropertyChanged + public void RaisePropertyChanged(string propName) + IsLoading = true; concept.
2. ActivityIndicator_Busy.IsVisible = false; (Direct control accessed)
These two approaches were mostly recommended and I went into depth of each since lot of hours in last few weeks. But nothing got crack.
What I achieved?:-
ActivityIndicator_Busy.IsVisible = false; concept is working smooth only when I put return before executing the statements (for testing purpose); statement on Button Clicked event. (Attached Image)
But as soon as I remove the return; On Pressing Button, directly after some pause, the HomePage Opens.
MY Questions:-
1. This is particular with the current scenario how to get the ActivityIndicator run Immediately when user clicks the Welcome Button.
2. Pertaining to same, When app starts there is also a blank white screen coming for few seconds almost 30 seconds which I also I want to show ActivityIndicator. But dont know how to impose that logic at which instance.
My Inputs
My MainPage.xaml File:-
(Edited 06-Sept-2018 09.11 pm)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Name="page_main_page"
NavigationPage.HasBackButton="False"
NavigationPage.HasNavigationBar="False"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:appNutri"
BindingContext="{x:Reference page_main_page}"
x:Class="appNutri.MainPage">
<ContentPage.Content>
<StackLayout BackgroundColor="White"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<StackLayout>
<Image x:Name="Image_Welcome"
Source="welcome.png"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
WidthRequest="300"
HeightRequest="300" />
<Button x:Name="Button_Welcome"
Clicked="Button_Welcome_Clicked"
Text="Welcome!"
BackgroundColor="DeepSkyBlue"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
TextColor="White"
HeightRequest="60" />
</StackLayout>
<StackLayout BackgroundColor="White"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<ActivityIndicator
x:Name="ActivityIndicator_Busy"
Color="Black"
IsEnabled="True"
HorizontalOptions="Center"
VerticalOptions="Center"
IsRunning="{Binding Source={x:Reference page_main_page}, Path=IsLoading}"
IsVisible="{Binding Source={x:Reference page_main_page}, Path=IsLoading}" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
My MainPage.cs Code:-
(Edited on 06-Sept-2018 09.13 pm)
using appNutri.Model;
using SQLite;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace appNutri
{
public partial class MainPage : Xamarin.Forms.ContentPage, INotifyPropertyChanged
{
private bool isLoading;
public bool IsLoading
{
get
{
return isLoading;
}
set
{
isLoading = value;
RaisePropertyChanged("IsLoading");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = this;
}
protected async void Button_Welcome_Clicked(object sender, EventArgs e)
{
IsLoading = true;
await Select_Local_User_Information();
IsLoading = false;
}
private async Task Select_Local_User_Information()
{
IsLoading = true;
string where_clause = "";
try
{
Sql_Common.Database_Folder_Path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string Database_Full_Path = Path.Combine(Sql_Common.Database_Folder_Path, Sql_Common.Database_Name);
SQLiteConnection connection = new SQLiteConnection(Database_Full_Path);
//connection.DropTable<User_Master>();
//connection.Delete(connection.Table<User_Master>());
//connection.CreateTable<User_Master>(CreateFlags.ImplicitPK | CreateFlags.AutoIncPK);
connection.CreateTable<User_Master>();
int count = connection.ExecuteScalar<int>("Select count(*) from User_Master");
if (count == 0)
{
connection.DropTable<User_Master>();
connection.CreateTable<User_Master>();
//IsLoading = false;
//IsBusy = false;
await Navigation.PushAsync(new User_Register_Page());
}
else
{
Sql_Common.User_Logged = true;
var Local_User_Data = connection.Table<User_Master>().ToList();
User_Master.Logged_User_Details_Container.First_Name = Local_User_Data[0].First_Name;
User_Master.Logged_User_Details_Container.Cell1 = Local_User_Data[0].Cell1;
where_clause = " Upper ( First_Name ) = " + "'" + User_Master.Logged_User_Details_Container.First_Name.ToUpper().Trim() + "'" + " and " +
" Cell1 = " + "'" + User_Master.Logged_User_Details_Container.Cell1.Trim() + "'";
int records = Sql_Common.Get_Number_Of_Rows_Count("User_Master", where_clause);
if (records == 0)
{
connection.DropTable<User_Master>();
connection.CreateTable<User_Master>();
IsLoading = false;
await Navigation.PushAsync(new User_Register_Page());
}
else
{
User_Master.User_Master_Table(where_clause, User_Master.Logged_User_Details_Container);
IsLoading = false;
await Navigation.PushAsync(new User_Home_Page());
}
}
connection.Close();
}
catch (SQLiteException ex)
{
string ex_msg = ex.Message;
}
IsLoading = false;
}
}
}
04-Oct-2018
Finally resolved with This Answer
Update 2018-09-10
You think that you have implemented INotifyPropertyChanged by adding INotifyPropertyChanged to your class definition and adding the event
public event PropertyChangedEventHandler PropertyChanged;
along with its event invocator
public void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Anyway, since ContentPage already implements INotifyPropertyChanged, adding those did not implement INotifyPropertyChanged. ContentPage already defines the event (or rather BindableObjectfrom which ContentPage indirectly inherits). Any object that relies on being informed about property changes in your page will subscribe to the PropertyChanged event of the ancestor and not the PropertyChanged event you defined, hence the ActivityIndicator will not update.
Just remove the event you defined and call OnPropertyChanged instead of RaisePropertyChanged() and you should be fine.
private bool isLoading;
public bool IsLoading
{
get
{
return isLoading;
}
set
{
isLoading = value;
OnPropertyChanged();
}
}
Since OnPropertyChanged is declared as
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
you don't have to pass the property name by hand. The compiler will do that for you beacsue of the CallerMemberNameAttribute.
End Update
The XAML extension {Binding IsLoading} binds the ActivityIndicator to the BindingContext of your page. By default the BindingContext is null, hence there is nothing to bind to and all your efforts are to no avail.
With a viewmodel
The preferred solution would be to use a viewmodel and assign it to MainPage.BindingContext, e.g.
var page = new MainPage()
{
BindingContext = new MainPageViewModel()
}
but if you take that road, you should move all of your UI logic to that viewmodel and encapsulate your SQL access and business logic in other classes, to keep the viewmodel clean from resource accesses and business logic. Having the resource accesses and logic in code behind may work for that small example, but is likely to become an unmaintainable mess.
Without a viewmodel
Anyway, you don't have to use a viewmodel to use bindings. You can set the BindingContext for the page (or some children) or use the Source of the BindingExtension
Setting the BindingContext
The BindingContext is passed from any page or view to it's children. You first have to give your page a name with x:Name="Page" (don't have to use Page, anyway, you can't use the class name of your page) and set the BindingContext to that page
<ContentPage ...
x:Name="Page"
BindingContext="{x:Reference Page}"
...>
now binding to IsLoading should work.
Using Source in the Binding
If you want to reference something else than the BindingContext of a view, BindingExtension has a property Source. You have to give a name to your page, too (see above)
<ContentPage ...
x:Name="Page"
...>
and now you can reference this in your binding
<ActivityIndicator
...
IsRunning="{Binding Path=IsLoading, Source={x:Reference Page}}"
IsVisible="{Binding Path=IsLoading, Source={x:Reference Page}}"/>

how to modify suavecontrols.materialforms in xamarin forms

I want to modify a entry made with the nugget package of this link https://github.com/SuavePirate/SuaveControls.MaterialFormControls
The declaration in xaml looks like this
<suave:MaterialEntry x:Name="Contrasena" IsPassword="True"
Placeholder="Password" Style="{StaticResource Entries}"/>
and I create and effect to remove the underline, I already checked with a normal entry so I know it works. The code with the effect looks like this
<suave:MaterialEntry x:Name="Contrasena" IsPassword="True"
Placeholder="Password" Style="{StaticResource Entries}"/>
<suave:MaterialEntry.Effects>
<local:NoUnderline></local:NoUnderline>
<suave:MaterialEntry.Effects>
</suave:MaterialEntry>
I want to know why don't work, I suppose is because of the namespace where are each one.
This is what I put in contentpage to use materialforms in the xaml
xmlns:suave="clr-namespace:SuaveControls.MaterialForms;Assembly=SuaveControls.MaterialForms"
This is what is in the top of the xaml project
<ContentPage xmlns="xamarin.com/schemas/2014/forms"
xmlns:x="schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-
namespace:Proyecto" x:Class="Proyecto.Registro" BackgroundColor="
{StaticResource GrisClaro}" xmlns:suave="clr-namespace:SuaveControls.MaterialForms;assembly=SuaveControls.MaterialForms">
And the local effect is like this in the pcl
public class NoUnderline : RoutingEffect
{
public NoUnderline() : base("EffectsSample.NoUnderline")
{
}
}
And like this in android
public class NoUnderline : PlatformEffect
{
protected override void OnAttached()
{
try
{
if (Control != null)
{
if (Android.OS.Build.VERSION.PreviewSdkInt >= (int)Android.OS.BuildVersionCodes.Lollipop)
Control.BackgroundTintList = Android.Content.Res.ColorStateList.ValueOf(Android.Graphics.Color.White);
else
{
Control.Background.SetColorFilter(Android.Graphics.Color.White, PorterDuff.Mode.SrcAtop);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
}
}
protected override void OnDetached()
{
}
}

UWP Change loading state using binding and async functions

I am coming from an Angular 2 and a C# back end background, so for the Angular side of things I am used to working with async functions and code, as well the C# background I understand the base libraries.
I am trying to create a simple page that has a a button, and a loading gif. You click the button the loading gif appears, 10 seconds later it disappears.
I can make the loading start no problem, but the nature of the async code jumps the execution and instantly makes the gif disappear.
How do I go about starting the spinner / making a gif visible, waiting 10 seconds in a non ui-blocking manner, and then finish with a thread-safe way of ending the animation / gif visibility?
View-Model code:
public class LoadingViewModel: INotifyPropertyChanged
{
private Visibility _loadingState;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public LoadingViewModel()
{
this._loadingState = Visibility.Collapsed;
}
public Visibility LoadingState
{
get {
return this._loadingState;
}
set {
this._loadingState = value;
this.OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
MainView.xaml.cs:
public LoadingViewModel LoadingViewModel { get; set; }
public MainPage()
{
this.InitializeComponent();
this.LoadingViewModel = new LoadingViewModel();
}
private async Task BeginLoading()
{
LoadingViewModel.LoadingState = Visibility.Visible;
await Task.Factory.StartNew(() =>
{
Task.Delay(TimeSpan.FromSeconds(10));
}).ContinueWith(EndLoadingState);
}
//Updated and works but is there a better way?
private async Task BeginLoading()
{
LoadingViewModel.LoadingState = Visibility.Visible;
await Task.Factory.StartNew(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(10));
await EndLoadingState(); //<-- New EndLoadingState doesn't accept parms
});
}
private async void EndLoadingState(object state)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
LoadingViewModel.LoadingState = Visibility.Collapsed;
});
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await BeginLoading();
}
And lastly a basic stack panel with my button and image:
<StackPanel Margin="10,144,0,144">
<Button Content="Begin Loading for 10 seconds" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0" Height="157" Width="366" FontSize="22" Background="{x:Null}" BorderThickness="5" BorderBrush="#FF58FF00" Click="Button_Click"/>
<Image HorizontalAlignment="Center" Height="250" VerticalAlignment="Center" Width="250" Margin="0,25,0,0" Stretch="UniformToFill" Source="Assets/LoadingBubbles.gif" Visibility="{x:Bind Path=LoadingViewModel.LoadingState, Mode=TwoWay}"/>
</StackPanel>
First, try using a bool property in your LoadingViewModel instead of Visibility as the latter is a UI attribute. You generally don't want that in your ViewModel. If your target version of Windows 10 is 14393 or higher, you can bind it directly without a BoolToVisibilityConverter. And the binding doesn't need to be TwoWay also.
Visibility="{x:Bind Path=LoadingViewModel.IsLoading, Mode=OneWay}"
Second, XAML binding will actually take care of dispatching the updated value onto the UI thread. So you can also get rid of Dispatcher.RunAsync and have a normal void method
private void EndLoadingState(object state)
{
LoadingViewModel.IsLoading = false;
}
Finally, your BeginLoading method(best to rename it to BeginLoadingAsync) can be simplified to
private async Task BeginLoadingAsync()
{
LoadingViewModel.IsLoading = true;
await Task.Delay(TimeSpan.FromSeconds(10));
EndLoadingState();
}
Hope this helps!

"cancel" disapears after submit SearxhBox if the Text is set to null (Xamarin.Forms)

I have a custom control for to show the "cancel" button inside the iOS SearchBars:
[assembly: ExportRenderer(typeof(SearchBar), typeof(AppSearchBarRenderer))]
namespace Elfo.VisionMobile.iOS.Renderers
{
public class AppSearchBarRenderer : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
Control.ShowsCancelButton = true;
}
}
}
And this is my XAML:
<SearchBar Text="{Binding SearchKey, Mode=TwoWay}" SearchCommand="{Binding SearchCommand}"/>
And the search execute method of the command (C#):
private void SearchExecute()
{
// whatever...
SearchKey = null;
}
And when I set the SearchKey to null or string.Empty , the "cancel" button disapears.
Guys, do you know something?
Ok I just fixed it.
I had to use the method OnElementPropertyChanged instead of OnElementChanged.

Pushpin location binding in windows phone 8 app is not working

i am developing a windows phone 8 app, in which i have to display a pushpin to current location.
while running the code it is showing an exception:
"An exception of type 'System.NullReferenceException' occurred in MapTestApp.DLL but was not handled in user code"
public MainPage()
{
InitializeComponent();
geolocator = new Geolocator();
geolocator.DesiredAccuracy = PositionAccuracy.High;
geolocator.MovementThreshold = 100; // The units are meters.
geolocator.StatusChanged += geolocator_StatusChanged;
geolocator.PositionChanged += geolocator_PositionChanged;
}
void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
pushpin1.DataContext = args.Position.Coordinate;
});
}
.xaml code:
<maps:Map x:Name="MyMap" Center="{Binding}" ZoomLevel="15">
<toolkit:MapExtensions.Children>
<toolkit:Pushpin x:Name="pushpin1" GeoCoordinate="{Binding}">
<toolkit:Pushpin.Template>
<ControlTemplate TargetType="toolkit:Pushpin">
<StackPanel>
<ContentPresenter x:Name="content" Content="{TemplateBinding Content}" HorizontalAlignment="Left"></ContentPresenter>
<Path Data="M0,0L1,1L2,0L2,0L1,0L0,0Z"
Fill="#00AAFF"
Stretch="Fill"
Margin="-2,0"
Height="120"
Width="30"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Visibility, Mode=TwoWay}"
HorizontalAlignment="Left"
/>
</StackPanel>
</ControlTemplate>
</toolkit:Pushpin.Template>
</toolkit:Pushpin>
</toolkit:MapExtensions.Children>
</maps:Map>
Here is the link to the exception screen, it shows pushpin as null object: https://docs.google.com/file/d/0By0Y-Dca1cKjYXp4T3ctV1hLUEk/edit?usp=sharing
Please try this attached property
public static class PushPinExtension
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.RegisterAttached("ItemsSource",
typeof(IEnumerable), typeof(PushPinExtension),
new PropertyMetadata(OnPushPinPropertyChanged));
private static void OnPushPinPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
var pushpin = MapExtensions.GetChildren((Map)uie).OfType<MapItemsControl>().FirstOrDefault();
if (pushpin != null) pushpin.ItemsSource = (IEnumerable)e.NewValue;
}
public static IEnumerable GetItemsSource(DependencyObject obj)
{
return (IEnumerable)obj.GetValue(ItemsSourceProperty);
}
public static void SetItemsSource(DependencyObject obj, IEnumerable value)
{
obj.SetValue(ItemsSourceProperty, value);
}
}
dp:PushPinExtension.ItemsSource="{Binding Path=PushpinCollection}"

Resources