I am using a DisplayAlert like below in my project.
var answer = await DisplayAlert("Alert", "You are invited to join a group, would you like to accept it or not?", "Accept", "Reject");
if (answer)
{
//accept invitation
}
else
{
//reject invitation
}
Accept and Reject options are working fine. My problem is Reject option is executing when tapping on the background or device back arrow. Is it has a simple solution other than implementing Rg.Plugins.Popup?
I had a similar request once, and I quickly "solved it" with a workaround like this (using DisplayActionSheet):
bool isActionSelected = false;
while(!isActionSelected)
{
string action = await DisplayActionSheet ("You are invited to join a group, would you like to accept it or not?", null, null, "Accept", "Reject");
if (action == "Accept")
{
isActionSelected = true;
//do stuff
}
else if (action == "Reject")
{
isActionSelected = true;
//do stuff
}
else
{
isActionSelected = false;
}
}
This is not suggested, unless you are in a hurry.
So, I would suggest you creating a custom popupView, something like this
<ContentView x:Name="popupView" BackgroundColor="#C0808080" Padding="10, 0" IsVisible="false" AbsoluteLayout.LayoutBounds="0, 0, 1, 1" AbsoluteLayout.LayoutFlags="All">
<StackLayout VerticalOptions="Center" HorizontalOptions="Center">
<StackLayout Orientation="Vertical" HeightRequest="150" WidthRequest="200" BackgroundColor="White">
<Label x:Name="myLabel" TextColor="Black" Text="You are invited to join a group, would you like to accept it or not?" />
<Button Text="Accept" TextTransform="None" Clicked="AcceptClicked" />
<Button Text="Reject" TextTransform="None" Clicked="RejectClicked" />
</StackLayout>
</StackLayout>
</ContentView>
then in .cs
popupView.IsVisible = true;
when you want this to appear.
The only solution - Use DependencyService to implement the alert on each platform .
Interface in Forms
public interface IShowAlertService
{
Task<bool> ShowAlert(string title, string message, string ok, string cancel);
}
Android Implementation
[assembly: Dependency(typeof(ShowAlertService))]
namespace XamarinTableView.Droid
{
class ShowAlertService : IShowAlertService
{
TaskCompletionSource<bool> taskCompletionSource;
public Task<bool> ShowAlert(string title, string message, string ok, string cancel)
{
taskCompletionSource = new TaskCompletionSource<bool>();
Android.App.AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.Instance);
AlertDialog alert = dialog.Create();
alert.SetTitle("Title");
alert.SetMessage("Complex Alert");
alert.SetButton("OK", (c, ev) =>
{
// Ok button click task
Console.WriteLine("Okay was clicked");
taskCompletionSource.SetResult(true);
});
alert.SetButton2("CANCEL", (c, ev) => {
Console.WriteLine("Cancel was clicked");
taskCompletionSource.SetResult(false);
});
alert.Show();
return taskCompletionSource.Task;
}
}
}
iOS Implementation
[assembly: Dependency(typeof(ShowAlertService))]
namespace XamarinTableView.iOS
{
class ShowAlertService : IShowAlertService
{
TaskCompletionSource<bool> taskCompletionSource;
public Task<bool> ShowAlert(string title, string message, string ok, string cancel)
{
taskCompletionSource = new TaskCompletionSource<bool>();
var okCancelAlertController = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
//Add Actions
okCancelAlertController.AddAction(UIAlertAction.Create(ok, UIAlertActionStyle.Default, alert => {
Console.WriteLine("Okay was clicked");
taskCompletionSource.SetResult(true);
}));
okCancelAlertController.AddAction(UIAlertAction.Create(cancel, UIAlertActionStyle.Cancel, alert => {
Console.WriteLine("Cancel was clicked");
taskCompletionSource.SetResult(false);
}));
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
//Present Alert
viewController.PresentViewController(okCancelAlertController, true, null);
return taskCompletionSource.Task;
}
}
Consume in Forms
bool isOk = await DependencyService.Get<IShowAlertService>().ShowAlert("Alert", "You have been alerted", "OK", "Cancel");
if (isOk)
{
}
else
{
}
In this way clicking outside the alert will not trigger cancel event .
Refer to DisplayAlert Xamarim forms.
Related
I need a help..
I need to get data from database using Controller.. But nothing happens when I do it.
My api is php codeigniter and i'm using X-API-Key.. I want to use It in MVC structure, because I need my view be navigable.
HttpClient
public class HttpClient
{
private Dictionary<string, string> _headers;
private HttpClient restClient;
public HttpClient(Dictionary<string, string> headers)
{
if (headers != null) _headers = headers;
else _headers = new Dictionary<string, string>();
}
public string this[string Key]
{
get
{
return _headers[Key];
}
set
{
_headers[Key] = value;
}
}
///<Summary>
/// HTTP GET REQUEST
///</Summary>
public async Task<HttpResponse<T>> ExcecuteAsync<T>(Method method, string baseUrl, Dictionary<string, object> formdata = null)
{
var client = new RestClient(baseUrl);
var request = new RestRequest(method);
foreach(var header in _headers)
{
request.AddHeader(header.Key, header.Value);
}
if(formdata != null)
{
foreach(var item in formdata)
{
request.AddParameter(item.Key, item.Value);
}
}
IRestResponse response = null;
Exception ex = null;
try
{
response = await client.ExecuteTaskAsync(request);
}
catch(Exception _ex)
{
ex = _ex;
}
var httpresponse = new HttpResponse<T>
{
Response = response
};
if (response == null)
{
httpresponse.Status = new StatusResponse
{
code = -1,
message = $"Nao se pode obter resposta do servidor, stacktrace:{ex.StackTrace}"
};
return httpresponse;
}
var jsonresult = response.Content;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var telement = JsonConvert.DeserializeObject<T>(jsonresult);
httpresponse.Result = telement;
}else if(response.StatusCode == 0)
{
httpresponse.Status = new StatusResponse
{
code = 0,
message = $"Nao se pode obter resposta do servidor"
};
}
else
{
var telement = JsonConvert.DeserializeObject<StatusResponse>(jsonresult);
httpresponse.Status = telement;
}
return httpresponse; //null
}
This is my App.xaml - where i have my x-api-key
App.RestClient = new HttpClient(new Dictionary<string, string>
{
{"X-API-Key", "926ff4c0-d86e-421f-9192-82ac52650f5c" }
});
App.BaseUrl = "http://10.156.96.80/apiuthomi";
protected override async void OnStart()
{
var ListasHospitais = await new HospitalService(App.RestClient).all();
MainPage = new ListHospital(ListasHospitais);
}
This is ListHospital.xaml
<StackLayout>
<ListView
HasUnevenRows="True"
SeparatorVisibility="Default"
x:Name="ListasHospitais"
IsPullToRefreshEnabled="True"
Margin="10">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="10" BackgroundColor="White" Margin="0 , 0 , 0, 10">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="1"
VerticalOptions="End"
Text="{Binding Nome}"
MaxLines="1"
LineBreakMode="TailTruncation"
FontAttributes="Bold"
/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding Detalhes}"
VerticalOptions="Start"
LineBreakMode="TailTruncation"
MaxLines="2"
/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
This is my ListHospital.xaml.cs
public partial class ListHospital : ContentPage
{
public ObservableCollection<Cliente> _clientes;
public ListHospital(List<Cliente>clientes)
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
_clientes = new ObservableCollection<Cliente>();
ListasHospitais.ItemsSource = _clientes;
foreach(var cliente in clientes)
{
_clientes.Add(cliente);
}
}
public void voltar_Click(object sender, System.EventArgs e)
{
Navigation.PopAsync();
}
}
HospitalService = where i will write all methods
public class HospitalService
{
private HttpClient _restclient;
public HospitalService(HttpClient restclient)
{
if (restclient != null) _restclient = restclient;
else throw new NullReferenceException("http nao pode ser nulo");
}
public async Task<List<Cliente>>all()
{
var response = await _restclient.GetAsync<List<Cliente>>("http://10.156.96.80/apiuthomi/cliente/all");
if (response.Result != null) return response.Result;
return new List<Cliente>();
}
}
until here everything is going well and it's working.. but the problem is that my view listHospital os no longer clickable.. like I want tho make this page be navigable...
this public ListHospital(List<Cliente>clientes)does not allow the clicked method to be valid in my view
I can't do await Navigation.PushAsync(new ListHospital()) , It give
me an error
this is because your ListHosptial page does not have a default (empty) constructor and you are not passing the required data. You can fix this by adding an default constructor
public ListHospital()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
...
}
if you do this then ListHospital needs to load it's own data. The easiest way to do this is probably to add a call to your services in the OnAppearing method
public override async void OnAppearing()
{
// call services to load data and assign ItemsSource
var data = await new HospitalService(App.RestClient).all();
ListasHospitais.ItemsSource = data;
}
I have a xamarin forms app which contains a pie graph using microcharts. The data for the graph is obtained from Web API. Everything works fine.Every time graph loads data from web API, there is some delay.So the appearance of graph will also takes time. What I am trying to do is use Lottie animation as the loading indicator before the graph appears.The animation shows but it gets flickered and load the graph.
What I have done
My xaml
<Grid>
<forms1:AnimationView
x:Name="AnimationView"
Animation="graphloading.json"
AutoPlay="True"
Margin="10"
BackgroundColor="Transparent"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" />
<forms:ChartView x:Name="Chart1" isVisible="False"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"/>
</Grid>
My Xaml.cs file
protected override async void OnAppearing()
{
await LoadDynamicGraph();
}
private async Task LoadDynamicGraph()
{
await Task.Run(() =>
{
// Graph Loading API call code
if (GraphDataObj[0] != null)
{
Device.BeginInvokeOnMainThread(() =>
{
AnimationView.IsPlaying = false;
AnimationView.IsVisible = false;
Chart1.IsVisible = true;
foreach (var item in GraphDataObj[0].DailyScore)
{
FirstGraphData = new List<ChartEntry>
{
new ChartEntry(float.Parse(item.CompletedItems))
{
Label = "Completed",
// ValueLabel = item.CompletedItems,
Color = SKColor.Parse("#c90484"),
TextColor = SKColor.Parse("#FFFFFF"),
},
new ChartEntry(float.Parse(item.TotalOpenItems))
{
Label = "Total ",
// ValueLabel = item.TotalOpenItems,
Color = SKColor.Parse("#00a8f3"),
TextColor = SKColor.Parse("#FFFFFF"),
}
};
}
Chart1.Chart = new PieChart()
{
Entries = FirstGraphData,
BackgroundColor = SKColor.Parse("#002F4F4F")
};
});
}
});
}
Currently the animation simply just appears and go. I want to make it show some time and then load the graph. Any help is appreciated.
Set this
AnimationView.IsPlaying = false;
AnimationView.IsVisible = false;
Chart1.IsVisible = true;
on the end of your function. It will still be pretty fast. U can try to set timer once.
finish = true;
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
If(finish){
finish = false;
return true;}
//there turn off your animation
return false;
});
I want to change my label text value of the previous page when back button pressed from the new page. I use messagecenter feature from new page and redirect the code flow to the RefreshCustomerDetails() in viewmodel.
I tried like below.
string _fullname = "";
public string FullName
{
protected set
{
if (_fullname != value)
{
_fullname = value;
OnPropertyChanged("FullName");
}
}
get { return _fullname; }
}
public void RefreshCustomerDetails()
{
//FullName = null;
FullName = Application.Current.Properties["customerFullName"].ToString();
}
<Label
x:Name="title_label"
Text="{Binding FullName}"
Font="Bold,18"
TextColor="Black"
Margin="-20,0,0,0"
HorizontalOptions="CenterAndExpand"
VerticalOptions="Center"/>
Taking the Fullname value from Local db and Bind it like above code, but no change in the name when back buttoon press. Try assign null value, that is also not working.
Any correction on my code?
Your code looks good, it should work. Does your ViewModel inherate from INotifyPropertyChanged? This is required for OnPropertyChanged():
public class MyViewModel : INotifyPropertyChanged
{
// stuff ...
}
Updated the code like below, remove RefreshCustomerDetails()
and add Device.BeginInvokeOnMainThread inside of MessagingCenter.
Text="{Binding FullName,Mode=TwoWay}"
MessagingCenter.Subscribe<AddCustomerBySOPage>(this, "Refresh", (sender) =>
{
Device.BeginInvokeOnMainThread(() =>
{
FullName = Application.Current.Properties["customerFullName"].ToString();
});
});
This is a follow up from my previous question (UWP Problems Setting GridView Items source using x:Bind).
In that case I was using the random access file data source from Mirosoft's Data virtualization sample and the grid was never being populated. The problem there was that I was not raising the property changed event. The grid now works well.
My problem now is that instead of a grid view I am trying to use the data source in a flip view control. The data source does get initialized, the change property notification is raised but nothing is shown in the flip view.
Can a flip view display data from a virtual collection?
Here is my model's code:
public class PhotoFlipViewViewModel : Mvvm.ViewModelBase
{
private FileDataSource _PicturesCollection;
public FileDataSource PicturesCollection
{
get
{
return _PicturesCollection;
}
set
{
if (_PicturesCollection != value)
{
_PicturesCollection = value;
RaisePropertyChanged(() => PicturesCollection);
}
}
}
public PhotoFlipViewViewModel()
{
initdata();
}
async void initdata()
{
StorageLibrary pictures = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
string path = pictures.SaveFolder.Path;
var source = await FileDataSource.GetDataSoure(path);
if (source.Count > 0)
{
PicturesCollection = source;
}
}
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
{
System.Diagnostics.Debug.WriteLine("PhotoFlipViewViewModel::OnNavigatedToAsync - event fired.");
if ( SessionState.ContainsKey("SelectedPhotoObjectFromGrid"))
{
await Task.CompletedTask;
}
public override async Task OnNavigatedFromAsync(IDictionary<string, object> suspensionState, bool suspending)
{
if (suspending)
{
}
await Task.CompletedTask;
}
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
{
args.Cancel = false;
await Task.CompletedTask;
}
}
My FlipView in XAML:
<FlipView x:Name="PhotoOuterFlipView"
BorderBrush="Black"
BorderThickness="1"
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
ItemsSource="{x:Bind ViewModel.PicturesCollection, Mode=OneWay}"
ItemTemplate="{StaticResource PictureFlipViewTemplate}">
<FlipView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</FlipView.ItemsPanel>
</FlipView>
Thanks, any advice is appreciated.
I am using MVVM light in my windows phone 8.1 here is code
xaml
<TextBox Text="{Binding SearchText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="TextChanged">
<Core:InvokeCommandAction Command="{Binding SearchTextChanged}"></Core:InvokeCommandAction>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBox>
here is my VM
private RelayCommand _searchTextChanged;
/// <summary>
/// Gets the SearchTextChanged.
/// </summary>
public RelayCommand SearchTextChanged
{
get
{
return _searchTextChanged
?? (_searchTextChanged = new RelayCommand(
() =>
{
LoadContents(this.SearchText);
}));
}
}
each time text is changed SearchTextChanged command is firing properly but the text in SearchText property is not updated it is one character less. e.g. if text in textbox is A than SearchText contains null. If text in textbox is 'aaa' than text in SearchText is only 'aa' the last character is always missing.
Any Ideas?
OK, so based on the comments, here's the answer - don't use the InvokeCommandAction but the two-way binding.
<TextBox Text="{Binding SearchText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
In the viewmodel behind this, there's a property called SearchText which has a setter that can call the LoadContents method, something like this...
public string SearchText
{
get { return this.searchText; }
set
{
this.searchText = value;
NotifyPropertyChanged("SearchText");
LoadContents(this.searchText);
}
}
Every time the string in the TextBox changes, the setter is invoked and so is the LoadContents method.
Bumped into a very similar situation but the given workaround wasn't a solution in my case! So discovered another way to update the value.
The solution:
Pass the element as parameter to the command and update the source and then call the method.
public string SearchText
{
get { return this.searchText; }
set
{
this.searchText = value;
NotifyPropertyChanged("SearchText");
}
}
public RelayCommand SearchTextChanged
{
get
{
return _searchTextChanged
?? (_searchTextChanged = new RelayCommand(
() =>
{
someCommandAction();
}));
}
}
private void someCommandAction(object obj)
{
TextBox textBox = (obj as TextBox);
if (textBox != null)
{
var be = textBox.GetBindingExpression(TextBox.TextProperty);
if (be != null)
be.UpdateSource();
}
LoadContents(textBox.Text); //textBox.text or the SearchTextproperty itself
}
XAML part:
<TextBox x:Name="textBoxName" Text="{Binding SearchText,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="TextChanged">
<Core:InvokeCommandAction Command="{Binding SearchTextChanged}"
CommandParameter="{Binding ElementName=textBoxName}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBox>