I create a project with Xamarin.Forms via MVVM, in which I want to generate a popup that accepts two data via a data picker. The user should specify a start date and an end date. Since I already needed it elsewhere, I already downloaded the ACR.UserDialogs NuGet package. I have also set up a DialogService in which the dialog receives three string parameters and would actually like a similar solution for the data picker. Does anyone have any idea how I can go about this?
Nuget packages: rg.plugins.popup, propertychanged.fody
MainView.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:datepickerpopup="clr-namespace:DatePickerPopup"
x:Class="DatePickerPopup.MainPage">
<ContentPage.BindingContext>
<datepickerpopup:MainViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Button Text="Open Popup"
Command="{Binding OpenPopup_Command}"
/>
<Label Text="{Binding SetDates_.StartDate}"/>
<Label Text="{Binding SetDates_.EndDate}"/>
</StackLayout>
</ContentPage>
MainViewModel.cs
using Rg.Plugins.Popup.Services;
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace DatePickerPopup
{
public class MainViewModel : BaseViewModel
{
public SetDates SetDates_ { get; set; } = new SetDates();
public Command OpenPopup_Command => new Command(async () =>
{
DatePickerPop pop = new DatePickerPop();
var vm = pop.BindingContext as DatePickerViewModel;
vm.Save_Command = PopupSave_Command;
await PopupNavigation.Instance.PushAsync(pop);
});
public Command PopupSave_Command => new Command(async(param) =>
{
SetDates_ = param as SetDates;
await PopupNavigation.Instance.PopAllAsync();
});
}
}
DatePickerPop.xaml
<?xml version="1.0" encoding="utf-8" ?>
<animations:PopupPage xmlns:animations="http://rotorgames.com"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:datepickerpopup="clr-namespace:DatePickerPopup"
x:Class="DatePickerPopup.DatePickerPop">
<animations:PopupPage.BindingContext>
<datepickerpopup:DatePickerViewModel/>
</animations:PopupPage.BindingContext>
<StackLayout VerticalOptions="Center"
HorizontalOptions="Center"
BackgroundColor="White">
<DatePicker Date="{Binding SetDates_.StartDate}"/>
<DatePicker Date="{Binding SetDates_.EndDate}"/>
<Button Text="Save"
Command="{Binding Save_Command}"
CommandParameter="{Binding SetDates_}"/>
<Button Text="Cancel"
Command="{Binding Cancel_Command}"/>
</StackLayout>
</animations:PopupPage>
DatePickerViewModel.cs
using Rg.Plugins.Popup.Services;
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace DatePickerPopup
{
public class DatePickerViewModel : BaseViewModel
{
public SetDates SetDates_ { get; set; } = new SetDates();
public Command Save_Command { get; set; }
public Command Cancel_Command => new Command(async () =>
await PopupNavigation.Instance.PopAsync());
}
}
SetDates.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace DatePickerPopup
{
public class SetDates : BaseViewModel
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
}
Related
After some research, I've learned that creating new HttpClients with every request is not a good use of HttpClient. Using Dependency Injection is one of the best solutions. So, I've implemented DI in my Xamarin Forms App, but I'm running into an issue. I have the client-side service as a Singleton and am injecting the httpclient into it. The httpclient timeout property works if the API is running. Unfortunately, when the API is not running, the timeout does not work. It takes over two minutes for an exception to occur even when I set the http timeout to 10 seconds. I'm expecting an exception to occur after the set timeout and a message of something like this, "No connection could be made because the target machine actively refused it...", but again nothing happens until over two minutes. However, if I create a new HttpClient, everything works as expected. I've initialized the timeout property in the Startup file and in the Service constructor, but there's no difference. When the API is not running, the httpclient's timeout does not work. I would really appreciate any help with this. See the link to the article I used as a reference below. Please let me know if you have any questions.
Thank you in advance!!!
Xamarin Forms Dependency Injection Article
EDIT:
Startup.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using Xamarin.Essentials;
namespace HttpClientTimeoutTest
{
public class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Init()
{
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
{
c.AddCommandLine(new string[] { $"ContentRoot={FileSystem.AppDataDirectory}" });
})
.ConfigureServices((c, x) => ConfigureServices(c, x))
.Build();
//Save our service provider so we can use it later.
ServiceProvider = host.Services;
}
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
services.AddHttpClient("", client =>
{
client.Timeout = TimeSpan.FromSeconds(10);
});
services.AddSingleton<IMyAppService, MyAppService>();
services.AddTransient<MainViewModel>();
}
}
}
MainViewModel.cs
using System;
using System.Windows.Input;
using Xamarin.Forms;
namespace HttpClientTimeoutTest
{
public class MainViewModel
{
public ICommand CallAPICommand
{
get
{
return new Command(async () =>
{
try
{
var races = await _appService.CallApiAsync();
}
catch (Exception ex)
{
}
});
}
}
IMyAppService _appService;
public MainViewModel(IMyAppService appService)
{
_appService = appService;
}
}
}
MyAppService.cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace HttpClientTimeoutTest
{
public class MyAppService : IMyAppService
{
private readonly HttpClient httpClient;
public MyAppService(IHttpClientFactory httpClientFactory)
//public MyAppService(HttpClient client)
{
httpClient = httpClientFactory.CreateClient();
//httpClient = client;
}
public async Task<string> CallApiAsync()
{
//HttpClient client = new HttpClient();
var response = await httpClient.GetAsync("YOUR API CALL HERE");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return null;
}
}
}
IMyAppService.cs
using System.Threading.Tasks;
namespace HttpClientTimeoutTest
{
public interface IMyAppService
{
Task<string> CallApiAsync();
}
}
MainPage.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace HttpClientTimeoutTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = Startup.ServiceProvider.GetService<MainViewModel>();
}
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HttpClientTimeoutTest.MainPage">
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Welcome to Xamarin.Forms!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
</Frame>
<Label Text="Start developing now" FontSize="Title" Padding="30,10,30,10"/>
<Label Text="Make changes to your XAML file and save to see your UI update in the running app with XAML Hot Reload. Give it a try!" FontSize="16" Padding="30,0,30,0"/>
<Label FontSize="16" Padding="30,24,30,0">
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="Learn more at "/>
<Span Text="https://aka.ms/xamarin-quickstart" FontAttributes="Bold"/>
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
<Button Command="{Binding CallAPICommand}" Text="Call API"/>
</StackLayout>
</ContentPage>
WebAPI HttpClientTimeoutTestController
using Microsoft.AspNetCore.Mvc;
using System.Threading;
namespace WebAPI.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class HttpClientTimeoutTestController : ControllerBase
{
public HttpClientTimeoutTestController()
{
}
[HttpGet]
public IActionResult Get()
{
//Thread.Sleep(60000);
return Ok(true);
}
}
}
Here's my solution. So I removed the Startup.cs file from my project and simply registered my Service as a Singleton:
App.xaml.cs
using System;
using System.Net.Http;
using Xamarin.Forms;
namespace HttpClientTimeoutTest
{
public partial class App : Application
{
public HttpClient Client { get; set; }
public App()
{
InitializeComponent();
Client = new HttpClient();
Client.Timeout = TimeSpan.FromSeconds(10);
var appService = new MyAppService(Client);
DependencyService.RegisterSingleton<IMyAppService>(appService);
MainPage = new MainPage();
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
}
Then, I assign the value of my Singleton to a variable to be injected in my ViewModel like below:
MainPage.xaml.cs
using Xamarin.Forms;
namespace HttpClientTimeoutTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
var service = DependencyService.Get<IMyAppService>();
BindingContext = new MainViewModel(service);
}
}
}
I've tested and retested this and my timeouts work perfectly even when the API is not running. I've also implemented this into my "REAL" app and everything work there also. Including the header tokens on the httpclient being injected. If this can be simplified even more, please let me know, but so far so good. Thanks again #Axemasta.
i am trying to bind my MasterViewModel where i have initiated two original viewModel to one view. But i am not getting any data so i must be doing the binding wrong. I have found several post
I have tried
in Xaml
<Label
x:Name="SectionRequired"
Grid.Row="2"
HorizontalOptions="End"
IsVisible="{Binding PostViewModel.IsRequired, Source={x:Reference PostViewModel}}"
Text="{x:Static resources:AppResources.AlertRequired}"
TextColor="Red" />
And also followed this solution but i was getting an expcetion that its used lika markup extenstion 'local1:PostViewModel' is used like a markup extension but does not derive from MarkupExtension.
https://stackoverflow.com/questions/50307356/multiple-bindingcontexts-on-same-contentpage-two-different-views
My Master
class MasterPostsViewModel : BaseViewModel
{
public PostViewModel postViewModel { get; set; }
public CategoriesViewModel categoriesViewModel { get; set; }
public MasterPostsViewModel()
{
postViewModel = new PostViewModel();
categoriesViewModel = new CategoriesViewModel();
}
}
}
Conte page
I have set the binding to one field here and that works, buit having to do that for the whole page is not what i want.
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel();
NameRequired.IsVisible = ViewModel.postViewModel.IsRequired;
}
Can you help please
instead of
IsVisible="{Binding PostViewModel.IsRequired, Source={x:Reference PostViewModel}}"
just use
IsVisible="{Binding postViewModel.IsRequired}"
your property name is postViewModel is lower case
also, get rid of this line - it will break the binding you have setup in the XAML
NameRequired.IsVisible = ViewModel.postViewModel.IsRequired;
I am trying to download an image from the open weather org i have the following code.
I am using an Android Device to test my application and gave it the appropriate internet permissions.
I am using the example JSON here
https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22
I no my json decoding is working fine as I see the image name in the result set under the weather object.
public class Weather
{
public int id { get; set; }
public string main { get; set; }
public string description { get; set; }
public string icon { get; set; }
}
public async void GetWeather ()
{
result = await restServices.GetWeatherRequest();
lblweather.Text = result.main.temp.ToString();
string url = $"https://openweathermap.org/img/w/{result.weather[0].icon}.png";
imgWeather.Source = new UriImageSource
{
Uri = new Uri(url),
CachingEnabled = true,
CacheValidity = new TimeSpan(5, 0, 0, 0)
};
}
}
My Main Xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="WeatherApp.Views.WeatherMain">
<ContentPage.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<Image x:Name="imgWeather"></Image>
<Label x:Name="lblweather" ></Label>
</StackLayout>
</ContentPage.Content>
</ContentPage>
This is the URL that is created by my program which you will see is a valid point.
https://openweathermap.org/img/w/09d.png
This appears to have fixed my issue.
As the source of my graphics was from a secure (https) server, therefore, I need to change the following settings.
Android Options => Advanced
HttpClient implementation = Set to "Managed" option value
SSL/TLS implementation = set to "Native TLS 1.2+" option value
I am struggling to find the answer by myself, using previous Stackoverflow posts, youtube and google searching.
I am trying to learn how to use SQLite with xamarin forms.
Solution connection:
using SQLite;
namespace TestSQLite
{
public interface IDatabaseConnection
{
SQLiteAsyncConnection GetConnection();
}
}
Android specific connection (iOS is identical)
using SQLite;
using System.IO;
using TestSQLite;
using Xamarin.Forms;
[assembly: Dependency(typeof(DatabaseConnection))]
namespace TestSQLite
{
public class DatabaseConnection : IDatabaseConnection
{
public SQLiteAsyncConnection GetConnection()
{
var dbName = "TestDb.db3";
var path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments), dbName);
return new SQLiteAsyncConnection(path);
}
}
}
And the MainPage C# code:
using SQLite;
using Xamarin.Forms;
namespace TestSQLite
{
public class ControlledDrugs
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Drug { get; set; }
public double Volume { get; set; }
}
public class Users
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
public partial class MainPage : ContentPage
{
private SQLiteAsyncConnection _connection;
public MainPage()
{
InitializeComponent();
_connection = DependencyService.Get<IDatabaseConnection>().GetConnection();
}
protected override async void OnAppearing()
{
await _connection.CreateTableAsync<ControlledDrugs>();
await _connection.CreateTableAsync<Users>();
RefreshUsers();
RefreshDrugs();
base.OnAppearing();
}
async void OnAdd(object sender, System.EventArgs e)
{
var user = new Users { Name = UserInput.Text };
await _connection.InsertAsync(user);
}
void OnUpdate(object sender, System.EventArgs e)
{
}
void OnDelete(object sender, System.EventArgs e)
{
}
async void RefreshUsers()
{
var userlist = await _connection.Table<Users>().ToListAsync();
Userlistview.ItemsSource = userlist;
}
async void RefreshDrugs()
{
var druglist = await _connection.Table<ControlledDrugs>().ToListAsync();
Drugslistview.ItemsSource = druglist;
}
private void Userlistview_Refreshing(object sender, System.EventArgs e)
{
RefreshUsers();
Userlistview.EndRefresh();
}
}
}
I know the add to sqlite method works, firstly because a user on Stackoverflow helped me, and secondly a blank cell appears on the listview. But thats the issue, the cells are blank, no matter how many I add, all blank.
I can't seem to physically access the sqlite database on the emulator to open and investigate if the entries are being written or entered as blanks. System.Environment.SpecialFolder.MyDocuments does not seem to save the .db3 in the emulator My Documents - separate issue, but limiting me to find the answer myself.
So i know the issue is either: 1)when the solution enters the data into the database (as blank) or if 2)the recall of data from the database to be viewed on the listview has the error.
Also, from my code you can probably see I am calling the refresh listview manually (by the user pulling the listview, because I am still learning and observable collection method/approach is a bit beyond me ATM.
Thanks team
UPDATE: Xaml code as requested: Thank you.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TestSQLite"
x:Class="TestSQLite.MainPage">
<StackLayout>
<Label Text="User Input"></Label>
<Entry x:Name="UserInput"></Entry>
<Button Text="add it" Clicked="OnAdd"></Button>
<Label Text="User"></Label>
<ListView x:Name="Userlistview" IsPullToRefreshEnabled="True" Refreshing="Userlistview_Refreshing"></ListView>
<Label Text="Drugs"></Label>
<ListView x:Name="Drugslistview"></ListView>
</StackLayout>
</ContentPage>
I'm a bit late, but hopefully this will help someone in the future (it would have certainly helped me!)
I ran into this same problem while working through the Xamarin tutorials on Microsoft's site. The tutorial first had you save a list to files, then changed to using the SQLite database. When I switched I found that adding a new record populated a blank list entry.
The culprit turned out to be in the binding between the data entry page, the list view and the variable names in the class. I had the class defined as:
public class Player
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string PlayerName { get; set; }
public DateTime JoinDate { get; set; }
}
When performing data entry I SHOULD have had:
<StackLayout Margin="20">
<Editor Placeholder="Enter player name"
Text="{Binding PlayerName}"
HeightRequest="50" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnRosterEntrySaveButtonClicked"
Grid.Row="1" />
<Button Text="Delete"
Clicked="OnRosterEntryDeleteButtonClicked"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
</StackLayout>
Instead I had "Text = "{Binding Text}" in the Editor. This didn't generate an error on build. I also had an error in the list view. What I SHOULD have had was:
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding PlayerName}"
Detail="{Binding Date}" />
</DataTemplate>
</ListView.ItemTemplate>
Instead I again had "Text" instead of "PlayerName". Note above that "Detail="{Binding Date}" is also wrong. The variable in the class is actually JoinDate. The above binding doesn't generate an error, however when the app runs no data is shown. Changing the binding to JoinDate and re-building allows the data to be shown.
My recommendation would be to check your bindings for setting and displaying the data on those pages.
public partial class HTCmds : ResourceDictionary
{
private ICanvasService mCanvasService;
[Dependency]
public ICanvasService CanvasService
{
get { return mCanvasService; }
set { mCanvasService = value; }
}
public HTCmds()
{
CopyCommand = new DelegateCommand<object>(this.Copy, this.CanCopy);
ExitCommand = new DelegateCommand<object>(this.Exit);
}
public DelegateCommand<object> CopyCommand { get; private set; }
public DelegateCommand<object> ExitCommand { get; private set; }
}
Resource Dictionary Xaml:
<ResourceDictionary x:Class="HTCmds"
x:ClassModifier="public"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:commands="clr-namespace:Commands;assembly=UIInfrastructure"
xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
xmlns:local="clr-namespace:Commands.Commands">
<local:HTCmds x:Key="thisobj"/>
<commands:CommandReference x:Key="CopyCommandReference" Command="{Binding Source={StaticResource thisobj}, Path=CopyCommand}"/>
<commands:CommandReference x:Key="ExitCommandReference" Command="{Binding Source={StaticResource thisobj}, Path=ExitCommand}"/>
</ResourceDictionary>
I've registered the ICanvasService but it's not getting injected in this class. Resource Dictionary is merged in the xaml file of a windows class:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Commands/HTCmds.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Is there something specific with ResourceDictionary class?
Thanks & Regards,
Vishal.
Your HTCmds object is created by WPF by this line of XAML:
<local:HTCmds x:Key="thisobj"/>
WPF has no knowledge of Unity so it does not know how to resolve the dependencies using Unity. You need to resolve objects using UnityContainer.Resolve. You can't rely on WPF to do this for you.