A Microsoft framework for building cross-platform mobile apps using .NET and C# with native performance and user interfaces.
Hello,
Welcome to our Microsoft Q&A platform!
Based on your code, I created a demo and modified your database HFNDatabase . The insert and
You can refer to the following code:
- HFNDatabase.cs public class HFNDatabase
{
static SQLiteAsyncConnection database;public static readonly AsyncLazy<HFNDatabase> Instance = new AsyncLazy<HFNDatabase>(async () => { var instance = new HFNDatabase(); CreateTableResult result = await database.CreateTableAsync<Models.OperatorModel>(); return instance; }); public HFNDatabase() { database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags); } public Task<List<Models.OperatorModel>> GetOperatorsAsync() { return database.Table<Models.OperatorModel>().ToListAsync(); } public Task<int> DeleteItemAsync(Models.OperatorModel item) { return database.DeleteAsync(item); } public Task<int> SaveItemAsync(Models.OperatorModel item) { if (item.OperatorID != 0) { return database.UpdateAsync(item); } else { return database.InsertAsync(item); } } }
Constants.cs
public static class Constants
{
public const string DatabaseFilename = "OperatorsSQLite.db3";
public const SQLite.SQLiteOpenFlags Flags =
// open the database in read/write mode
SQLite.SQLiteOpenFlags.ReadWrite |
// create the database if it doesn't exist
SQLite.SQLiteOpenFlags.Create |
// enable multi-threaded database access
SQLite.SQLiteOpenFlags.SharedCache;
public static string DatabasePath
{
get
{
var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return Path.Combine(basePath, DatabaseFilename);
}
}
}
AsyncLazy.cs
public class AsyncLazy<T> : Lazy<Task<T>>
{
readonly Lazy<Task<T>> instance;
public AsyncLazy(Func<T> factory)
{
instance = new Lazy<Task<T>>(() => Task.Run(factory));
}
public AsyncLazy(Func<Task<T>> factory)
{
instance = new Lazy<Task<T>>(() => Task.Run(factory));
}
public TaskAwaiter<T> GetAwaiter()
{
return instance.Value.GetAwaiter();
}
}
OperatorModel.cs
public class OperatorModel: INotifyPropertyChanged
{
[PrimaryKey, AutoIncrement]
public int OperatorID { get; set; }
string _operatorName;
public string OperatorName
{
set { SetProperty(ref _operatorName, value); }
get { return _operatorName; }
}
public string OperatorPhone { get; set; }
public string OperatorEmail { get; set; }
public string OperatorAvatar { get; set; }
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
- In page
SelectOperatorPage.xaml, you can use data binding :<ContentPage.Content> <Grid BackgroundColor="White"> <Grid.RowDefinitions> <RowDefinition Height="40"/> <RowDefinition Height="40"/> <RowDefinition Height="80"/> <RowDefinition Height="*"/> <RowDefinition Height="50"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="50"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="50"/> </Grid.ColumnDefinitions> <Button x:Name="BackButton" Text="Back" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" TextColor="Black" FontAttributes="Bold" HeightRequest="40" VerticalOptions="CenterAndExpand" HorizontalOptions="End" Margin="0,0,20,0" Clicked="BackButton_Clicked"/> <Image Source="hfn256" Margin="2" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" HorizontalOptions="Center" BackgroundColor="Transparent"/> <Label Text="Select Operator..." FontSize="Large" TextColor="Black" FontAttributes="Bold" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" HorizontalOptions="Center" VerticalOptions="Start" Margin="20" BackgroundColor="Transparent"/> <ListView x:Name="OperatorListView" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" RowHeight="140" ItemSelected="OperatorListView_ItemSelected"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <ContentView> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="200"/> </Grid.ColumnDefinitions> <Image Source="{Binding OperatorAvatar}" Grid.Row="0" Grid.Column="0" HeightRequest="200" WidthRequest="100"/> <Label Text="{Binding OperatorName}" FontAttributes="Bold" HorizontalOptions="Start" VerticalOptions="Center" Grid.Row ="0" Grid.Column="1"/> <Label Text="{Binding OperatorEmail}" FontAttributes="Italic" FontSize="Micro" Grid.Row="0" Grid.Column="2" HeightRequest="30" HorizontalOptions="Start" VerticalOptions="Center"/> <Label Text="{Binding OperatorPhone}" FontAttributes="Italic" FontSize="Small" Grid.Row="0" Grid.Column="2" HeightRequest="30" HorizontalOptions="Start" VerticalOptions="End"/> </Grid> </ContentView> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> </ContentPage.Content>
SelectOperatorPage.xaml.cs
public partial class SelectOperatorPage : ContentPage
{
OperatorViewModel ovm;
public EventHandler<Models.OperatorModel> ReturnValue;
public SelectOperatorPage()
{
// add a bit of padding to cater to the "notch" on the iPhone.
if (Device.RuntimePlatform == Device.iOS)
{
Padding = new Thickness(0, 40, 0, 0);
}
InitializeComponent();
}
protected override async void OnAppearing()
{
base.OnAppearing();
//await ovm.GetOperators();
//OperatorListView.ItemsSource = OperatorViewModel.OperatorList;
HFNDatabase database = await HFNDatabase.Instance;
OperatorListView.ItemsSource = await database.GetOperatorsAsync();
}
private async void BackButton_Clicked(object sender, EventArgs e)
{
await Navigation.PopModalAsync();
}
private async void OperatorListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem != null)
{
Models.OperatorModel operatorModel = (Models.OperatorModel)e.SelectedItem;
//ovm.SelectedOperator = operatorModel;
EventHandler<Models.OperatorModel> handler = ReturnValue;
if (handler != null)
{
handler(this, operatorModel);
}
await Navigation.PopModalAsync();
}
}
}
- In NewOperatorPage.xaml.cs,we can create a new Operator by code
await database.SaveItemAsync(todoItem);[XamlCompilation(XamlCompilationOptions.Compile)] public partial class NewOperatorPage : ContentPage { public NewOperatorPage() { // add a bit of padding to cater to the "notch" on the iPhone. if (Device.RuntimePlatform == Device.iOS) { Padding = new Thickness(0, 40, 0, 0); } InitializeComponent(); } private async void BackButton_Clicked(object sender, EventArgs e) { await Navigation.PopModalAsync(); } private async void OKButton_Clicked(object sender, EventArgs e) { //var name = NameEntry.Text; //var Phone = PhoneEntry.Text; //var Email = EmailEntry.Text; //var Avatar = AvatarEntry.Text; //OperatorViewModel.OperatorList.Add(new OperatorModel(OperatorViewModel.OperatorList.Count, name, Phone, Email, Avatar)); //await Navigation.PopModalAsync(); var todoItem = (Models.OperatorModel)BindingContext; HFNDatabase database = await HFNDatabase.Instance; await database.SaveItemAsync(todoItem); await Navigation.PopModalAsync(); } private async void CancelButton_Clicked(object sender, EventArgs e) { await Navigation.PopModalAsync(); } }
In NewOperatorPage.xaml, we can also use data binding for the several Entry.
<ContentPage.Content>
<Grid BackgroundColor="White">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="40"/>
<RowDefinition Height="30"/>
<RowDefinition Height="10"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Button
x:Name="BackButton"
Text="Back"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
TextColor="Black"
FontAttributes="Bold"
HeightRequest="40"
VerticalOptions="CenterAndExpand"
HorizontalOptions="End"
Margin="0,0,20,0"
Clicked="BackButton_Clicked"/>
<Image
Source="hfn256"
Margin="2"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
HorizontalOptions="Center"
BackgroundColor="Transparent"/>
<Label
Text="Add Operator"
FontSize="Large"
TextColor="Black"
FontAttributes="Bold"
Padding =" 0, 0, 0, 0"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="3"
HorizontalOptions="Center"
VerticalOptions="End"
BackgroundColor="Transparent"/>
<ScrollView Orientation="Vertical"
Grid.Row="4" Grid.Column="0"
Grid.ColumnSpan="3">
<Grid BackgroundColor="White" >
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="100"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label
x:Name="NameLabel"
Text="Name: "
TextColor="Black"
FontSize="Medium"
FontAttributes="Bold"
Grid.Row="0"
Margin="40, 0, 0, 0"
HorizontalOptions="Start"
VerticalOptions="Center"/>
<Entry
x:Name="NameEntry"
Text="{Binding OperatorName}"
Placeholder="Enter a name / callsign"
PlaceholderColor="LightGray"
TextColor="DarkBlue"
FontSize="Medium"
Keyboard="Text"
Grid.Row="0"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="0, 0, 30, 0"/>
<Label
x:Name="PhoneLabel"
Text="Phone: +"
TextColor="Black"
FontSize="Medium"
FontAttributes="Bold"
Grid.Row="1"
Grid.Column="0"
Margin="40, 0, 0, 0"
HorizontalOptions="Start"
VerticalOptions="Center"/>
<!-- <Entry
x:Name="PhoneCountryEntry"
Placeholder="Country"
PlaceholderColor="LightGray"
TextColor="DarkBlue"
FontSize="Medium"
Keyboard="Numeric"
Grid.Row="1"
Grid.Column="1"
Margin="0, 0, 0, 0"/> -->
<Entry
x:Name="PhoneEntry"
Text="{Binding OperatorPhone}"
Placeholder="Phone (no spaces)"
PlaceholderColor="LightGray"
TextColor="DarkBlue"
FontSize="Medium"
Keyboard="Numeric"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="0, 0, 30, 0"/>
<Label
x:Name="EmailLabel"
Text="Email: "
TextColor="Black"
FontSize="Medium"
FontAttributes="Bold"
Grid.Row="2"
Margin="40, 0, 0, 0"
HorizontalOptions="Start"
VerticalOptions="Center"/>
<Entry
x:Name="EmailEntry"
Text="{Binding OperatorEmail}"
Placeholder="Enter Email..."
PlaceholderColor="LightGray"
TextColor="DarkBlue"
FontSize="Medium"
Keyboard="Email"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="0, 0, 30, 0"/>
<Label
x:Name="AvatarLabel"
Text="Avatar: "
TextColor="Black"
FontSize="Medium"
FontAttributes="Bold"
Grid.Row="3"
Margin="40, 0, 0, 0"
HorizontalOptions="Start"
VerticalOptions="Center"/>
<Entry
x:Name="AvatarEntry"
Text="{Binding OperatorAvatar}"
Placeholder="Enter Avatar URL..."
PlaceholderColor="LightGray"
TextColor="DarkBlue"
FontSize="Medium"
Keyboard="Url"
Grid.Row="3"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="0, 0, 30, 0"/>
<Button
x:Name="OKButton"
Text="OK"
TextColor="Black"
FontAttributes="Bold"
HorizontalOptions="Start"
VerticalOptions="Center"
WidthRequest="100"
HeightRequest="40"
Margin="0, 20, 0, 0"
Grid.Row="4"
Grid.Column="1"
Clicked="OKButton_Clicked"/>
<Button
x:Name="CancelButton"
Text="Cancel"
TextColor="Black"
FontAttributes="Bold"
HorizontalOptions="End"
VerticalOptions="Center"
WidthRequest="100"
HeightRequest="40"
Margin="0, 20, 40, 0"
Grid.Row="4"
Grid.Column="2"
Clicked="CancelButton_Clicked"/>
</Grid>
</ScrollView>
</Grid>
</ContentPage.Content>
- In
OperatorsPage.xaml.cs, I changed functionAddOperatorButton_Clickedto the following code. In this page, you can also use data binding forSelectedOperatorEntryEntry .private async void AddOperatorButton_Clicked(object sender, EventArgs e) { await Navigation.PushModalAsync(new NewOperatorPage { BindingContext = new OperatorModel() }); }
The whole code of OperatorsPage .xaml.cs
public partial class OperatorsPage : ContentPage
{
// OperatorViewModel ovm;
OperatorModel opm;
public EventHandler<OperatorModel> ReturnValue;
public OperatorsPage(OperatorModel SelectModel)
{
if (Device.RuntimePlatform == Device.iOS) { Padding = new Thickness(0, 40, 0, 0); }
InitializeComponent();
//if (SelectModel != null)
//{
// opm = SelectModel;
//}
//else
//{
// opm = new OperatorModel();
//}
opm = new OperatorModel();
BindingContext = opm;
}
private async void BackButton_Clicked(object sender, EventArgs e)
{
// The if statements below added at the behest of JessieZhang-2116 (Microsoft Q&A) 13/05/2021
if (SelectModel != null)
{
EventHandler<OperatorModel> handler = ReturnValue;
if (handler != null)
{
handler(this, opm);
}
}
await Navigation.PopModalAsync();
// The line above originally commented out at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021
}
private async void AddOperatorButton_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new NewOperatorPage {
BindingContext = new OperatorModel()
});
}
private async void SelectOperatorButton_Clicked(object sender, EventArgs e)
{
// The lines below added at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021
SelectOperatorPage page = new SelectOperatorPage();
page.ReturnValue += delegate (object s, OperatorModel operatorModel)
{
BackCall(s, operatorModel);
};
await Navigation.PushModalAsync(page);
}
// The line below added at the behest of LeonLu-MSFT (Microsoft Q&A) 06/05/2021
OperatorModel SelectModel;
// The lines below added at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021
private void BackCall(object s, OperatorModel model)
{
SelectModel = model;
opm.OperatorName = model.OperatorName;
}
// The lines included below in the DeleteOperatorButton_Clicked added at the behest of LeonLu-MSFT (Microsoft Q&A) 06/05/2021
private async void DeleteOperatorButton_Clicked(object sender, EventArgs e)
{
if (SelectModel != null)
{
bool answer = await DisplayAlert("Warning!", "Do you really want to delete Operator " + SelectModel.OperatorName, "Yes", "No");
if (answer == true)
{
OperatorViewModel.OperatorList.Remove(SelectModel);
SelectModel = null;
SelectedOperatorEntry.Text = "";
}
}
else
{
await DisplayAlert("Info", "Please select an Operator to delete", "OK");
}
}
// The lines below in UpdateOperatorButton_Clicked added at the behest of LeonLu-MSFT (Microsoft Q&A) 11/05/2021
private async void UpdateOperatorButton_Clicked(object sender, EventArgs e)
{
if (SelectModel != null)
{
var operatorInfoPage = new OperatorInfoPage(SelectModel);
operatorInfoPage.ReturnValue += delegate (object s, OperatorModel operatorModel)
{
BackCall(s, operatorModel);
};
await Navigation.PushModalAsync(operatorInfoPage);
}
else
{
await DisplayAlert("Info", "Please select an Operator", "OK");
}
}
}
Note:
I just modified some of your code which is relative to the fuction of GetOperatorsAsync and SaveItemAsync in database HFNDatabase. It works on my side.
You can change above code according to your requirement.
Best Regards,
Jessie Zhang
---
If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.
This is what I see.