Xamarin.Forms MVVM ViewModel 如何与数据库交互?
Xamarin.Forms MVVM How does the ViewModel Interact with the Database?
我对 MVVM 模式完全陌生,我已经到处寻找有关使用 MVVM 和 SQLite 的信息,但我仍然完全不知所措。我有一个项目有多个模型,这些模型在我的数据库中填充 tables。我有一个数据库服务,可以为我的 table 添加、编辑、设置和获取对象。 ViewModel 有一个 ObservableCollection 对象,它们出现在视图中,但它是如何在我的数据库中更新的?所以基本上,我不想在 ViewModel 中填充我的 ObservableCollection。我想从数据库中相应的 table 填充它。我只是不明白,如果 ViewModel 的 ObservableCollection 正在从 View 更新,那么什么会更新我的数据库中的信息?要添加,get 方法在我的数据库服务 returns table ToList 中。我考虑尝试在我的 ViewModel 中做一些事情,比如将 ObservableCollection 的 属性 设置为我的 get 方法,但这不起作用,因为一个是列表,一个是 ObservableCollection。对此的任何想法或建议都会非常有帮助。谢谢大家
如果我理解这个问题,您正在寻找数据库和您的视图绑定到的 ObservableCollection 之间的 two-way 粘合剂。我记得在那个“完全迷失”的地方,想要用简单的术语从头开始解释,并决定 post 稍微 longer-than-usual 回答,因为这对我有帮助。
如果您阅读了,我将解释如何插入、更新和删除记录以及查询 SQLite 本地数据库,在 ListView 中显示返回的记录集,如下所示:
一开始您的视图绑定可能是这样的:
<StackLayout>
<ListView ItemsSource="{Binding Recordset}" />
<Grid>
<Button Grid.Column="0"
Text="Query"
Command="{Binding QueryCommand}"/>
<Button Grid.Column="1"
Text="Clear"
Command="{Binding ClearCommand}" />
</Grid>
</StackLayout>
BindingContext 设置如下:
public partial class MainPage : ContentPage
{
public MainPage()
{
BindingContext = new MainPageBinding();
InitializeComponent();
}
}
class MainPageBinding : INotifyPropertyChanged
{
public ObservableCollection<Record> Recordset { get; } =
new ObservableCollection<Record>();
.
.
.
}
这将显示一个 Record/Model,看起来类似于:
[Table("items")]
class Record
{
[PrimaryKey]
public string guid { get; set; } = Guid.NewGuid().ToString().Trim().TrimStart('{').TrimEnd('}');
public string Description { get; set; } = string.Empty;
public override string ToString() => Description;
}
此时,我们放入 Recordset 中的每个新记录都会显示在视图中。具体来说,您想通过执行 SQLite 查询来创建这些记录。
public ICommand QueryCommand { get; private set; }
private void OnQuery(object o)
{
Recordset.Clear();
List<Record> queryResult;
using (var cnx = new SQLiteConnection(mockConnectionString))
{
queryResult = cnx.Query<Record>("SELECT * FROM items");
foreach (var record in queryResult)
{
Recordset.Add(record);
}
}
}
现在这只是一种方法,对吧?但我希望它能给你一些想法。如果您想试验一下,我已将我的工作示例上传到 GitHub。
现在换个方向。
(这将回答问题的一部分 如果 ViewModel 的 ObservableCollection 正在从 View 更新,那么什么会更新我的数据库中的信息?)
假设弹出一个描述编辑器,您可以编辑 SelectedItem
。提交后,它会设置 SQLite 记录的 Description
属性。
我们可以像这样模拟这种交互:
public ICommand EditCommand { get; private set; }
private void OnEdit(object o)
{
if(SelectedItem != null)
{
// Some kind of UI interaction that changes the bound record
SelectedItem.Description = $"{SelectedItem.Description} (Edited)";
// You'll need to decide what kind of UI action merits a SQL
// update, but when you're ready to do that here's the command:
using (var cnx = new SQLiteConnection(mockConnectionString))
{
cnx.Update(SelectedItem);
}
}
}
与此同时,我们修改了 Record
以实现 INotifyPropertyChanged
...
[Table("items")]
class Record : INotifyPropertyChanged
{
[PrimaryKey]
public string guid { get; set; } = Guid.NewGuid().ToString().Trim().TrimStart('{').TrimEnd('}');
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
string _Description = string.Empty;
public string Description
{
get => _Description;
set
{
if(_Description != value)
{
_Description = value;
OnPropertyChanged();
}
}
}
public override string ToString() => Description;
}
...并且由于我们希望视图更新 不仅对集合的更改而且对单个属性的编程更改 我们需要一个数据模板 xaml 现在可以正确显示绑定说明 属性:
<StackLayout>
<ListView ItemsSource="{Binding Recordset}"
SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Text="{Binding Description}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid>
<Button Grid.Column="0"
Text="Query"
Command="{Binding QueryCommand}"/>
<Button Grid.Column="1"
Text="Clear"
Command="{Binding ClearCommand}" />
<Button Grid.Column="2"
Text="Edit"
Command="{Binding EditCommand}" />
</Grid>
</StackLayout>
好的!所以 Select:
单击“编辑”按钮:
单击“清除”按钮:
现在单击“查询”按钮以确认数据库已更新。
INSERT 和 DELETE 操作
添加新记录:
public ICommand AddCommand { get; private set; }
private async void OnAdd(object o)
{
var result = await App.Current.MainPage.DisplayPromptAsync("New Item", "Describe the item");
if(!string.IsNullOrWhiteSpace(result))
{
var newRecord = new Record { Description = result };
Recordset.Add(newRecord);
using (var cnx = new SQLiteConnection(mockConnectionString))
{
cnx.Insert(newRecord);
}
}
}
要删除:
public ICommand DeleteSelectedCommand { get; private set; }
private void OnDeleteSelected(object o)
{
if (SelectedItem != null)
{
var removed = SelectedItem;
Recordset.Remove(SelectedItem);
using (var cnx = new SQLiteConnection(mockConnectionString))
{
cnx.Delete(removed);
}
}
}
搜索栏
要全面回答 ViewModel 如何与数据库交互? 还需要一件事:主页上的搜索栏,以便我们可以在新数据库:这是代码 post 在 GitHub 上编辑的最终版本。
搜索:
public ICommand SearchCommand { get; }
private void OnSearch(string expr)
{
Recordset.Clear();
List<Record> queryResult;
using (var cnx = new SQLiteConnection(mockConnectionString))
{
queryResult = cnx.Query<Record>($"SELECT * FROM items WHERE Description LIKE'%{expr}%'");
foreach (var record in queryResult)
{
Recordset.Add(record);
}
}
}
我对 MVVM 模式完全陌生,我已经到处寻找有关使用 MVVM 和 SQLite 的信息,但我仍然完全不知所措。我有一个项目有多个模型,这些模型在我的数据库中填充 tables。我有一个数据库服务,可以为我的 table 添加、编辑、设置和获取对象。 ViewModel 有一个 ObservableCollection 对象,它们出现在视图中,但它是如何在我的数据库中更新的?所以基本上,我不想在 ViewModel 中填充我的 ObservableCollection。我想从数据库中相应的 table 填充它。我只是不明白,如果 ViewModel 的 ObservableCollection 正在从 View 更新,那么什么会更新我的数据库中的信息?要添加,get 方法在我的数据库服务 returns table ToList 中。我考虑尝试在我的 ViewModel 中做一些事情,比如将 ObservableCollection 的 属性 设置为我的 get 方法,但这不起作用,因为一个是列表,一个是 ObservableCollection。对此的任何想法或建议都会非常有帮助。谢谢大家
如果我理解这个问题,您正在寻找数据库和您的视图绑定到的 ObservableCollection 之间的 two-way 粘合剂。我记得在那个“完全迷失”的地方,想要用简单的术语从头开始解释,并决定 post 稍微 longer-than-usual 回答,因为这对我有帮助。
如果您阅读了,我将解释如何插入、更新和删除记录以及查询 SQLite 本地数据库,在 ListView 中显示返回的记录集,如下所示:
一开始您的视图绑定可能是这样的:
<StackLayout>
<ListView ItemsSource="{Binding Recordset}" />
<Grid>
<Button Grid.Column="0"
Text="Query"
Command="{Binding QueryCommand}"/>
<Button Grid.Column="1"
Text="Clear"
Command="{Binding ClearCommand}" />
</Grid>
</StackLayout>
BindingContext 设置如下:
public partial class MainPage : ContentPage
{
public MainPage()
{
BindingContext = new MainPageBinding();
InitializeComponent();
}
}
class MainPageBinding : INotifyPropertyChanged
{
public ObservableCollection<Record> Recordset { get; } =
new ObservableCollection<Record>();
.
.
.
}
这将显示一个 Record/Model,看起来类似于:
[Table("items")]
class Record
{
[PrimaryKey]
public string guid { get; set; } = Guid.NewGuid().ToString().Trim().TrimStart('{').TrimEnd('}');
public string Description { get; set; } = string.Empty;
public override string ToString() => Description;
}
此时,我们放入 Recordset 中的每个新记录都会显示在视图中。具体来说,您想通过执行 SQLite 查询来创建这些记录。
public ICommand QueryCommand { get; private set; }
private void OnQuery(object o)
{
Recordset.Clear();
List<Record> queryResult;
using (var cnx = new SQLiteConnection(mockConnectionString))
{
queryResult = cnx.Query<Record>("SELECT * FROM items");
foreach (var record in queryResult)
{
Recordset.Add(record);
}
}
}
现在这只是一种方法,对吧?但我希望它能给你一些想法。如果您想试验一下,我已将我的工作示例上传到 GitHub。
现在换个方向。
(这将回答问题的一部分 如果 ViewModel 的 ObservableCollection 正在从 View 更新,那么什么会更新我的数据库中的信息?)
假设弹出一个描述编辑器,您可以编辑 SelectedItem
。提交后,它会设置 SQLite 记录的 Description
属性。
我们可以像这样模拟这种交互:
public ICommand EditCommand { get; private set; }
private void OnEdit(object o)
{
if(SelectedItem != null)
{
// Some kind of UI interaction that changes the bound record
SelectedItem.Description = $"{SelectedItem.Description} (Edited)";
// You'll need to decide what kind of UI action merits a SQL
// update, but when you're ready to do that here's the command:
using (var cnx = new SQLiteConnection(mockConnectionString))
{
cnx.Update(SelectedItem);
}
}
}
与此同时,我们修改了 Record
以实现 INotifyPropertyChanged
...
[Table("items")]
class Record : INotifyPropertyChanged
{
[PrimaryKey]
public string guid { get; set; } = Guid.NewGuid().ToString().Trim().TrimStart('{').TrimEnd('}');
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
string _Description = string.Empty;
public string Description
{
get => _Description;
set
{
if(_Description != value)
{
_Description = value;
OnPropertyChanged();
}
}
}
public override string ToString() => Description;
}
...并且由于我们希望视图更新 不仅对集合的更改而且对单个属性的编程更改 我们需要一个数据模板 xaml 现在可以正确显示绑定说明 属性:
<StackLayout>
<ListView ItemsSource="{Binding Recordset}"
SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Text="{Binding Description}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid>
<Button Grid.Column="0"
Text="Query"
Command="{Binding QueryCommand}"/>
<Button Grid.Column="1"
Text="Clear"
Command="{Binding ClearCommand}" />
<Button Grid.Column="2"
Text="Edit"
Command="{Binding EditCommand}" />
</Grid>
</StackLayout>
好的!所以 Select:
单击“编辑”按钮:
单击“清除”按钮:
现在单击“查询”按钮以确认数据库已更新。
INSERT 和 DELETE 操作
添加新记录:
public ICommand AddCommand { get; private set; }
private async void OnAdd(object o)
{
var result = await App.Current.MainPage.DisplayPromptAsync("New Item", "Describe the item");
if(!string.IsNullOrWhiteSpace(result))
{
var newRecord = new Record { Description = result };
Recordset.Add(newRecord);
using (var cnx = new SQLiteConnection(mockConnectionString))
{
cnx.Insert(newRecord);
}
}
}
要删除:
public ICommand DeleteSelectedCommand { get; private set; }
private void OnDeleteSelected(object o)
{
if (SelectedItem != null)
{
var removed = SelectedItem;
Recordset.Remove(SelectedItem);
using (var cnx = new SQLiteConnection(mockConnectionString))
{
cnx.Delete(removed);
}
}
}
搜索栏
要全面回答 ViewModel 如何与数据库交互? 还需要一件事:主页上的搜索栏,以便我们可以在新数据库:这是代码 post 在 GitHub 上编辑的最终版本。
搜索:
public ICommand SearchCommand { get; }
private void OnSearch(string expr)
{
Recordset.Clear();
List<Record> queryResult;
using (var cnx = new SQLiteConnection(mockConnectionString))
{
queryResult = cnx.Query<Record>($"SELECT * FROM items WHERE Description LIKE'%{expr}%'");
foreach (var record in queryResult)
{
Recordset.Add(record);
}
}
}