是否可以或实用将 XAML 中的 2 路数据绑定直接连接到基于 OpenApi 元数据文件的 Visual Studio 生成的 RESTApi。例如,Visual Studio 基于 OpenApi 文件或在 URL 上发布的元数据生成 REST 客户端。
MyRESTClient _client = new MyRESTClient("http://localhost", new System.Net.Http.HttpClient());
这可能会出现在某些 ViewModel 的顶部。假设我们正在使用一个相对的 REST 端点,例如:
/api/v1/小猫
不带参数的 Get 方法的实现返回所有 Kittens。 Visual Studio RESTClient 生成器将为所有端点上可用的对象生成完整的 REST API。现在假设命名空间与应用程序相同,我们有一个 Kittens 对象。
我可以在 ViewModel 中定义一个名为 _kittens 的 Kittens 对象,如下所示:
private ObservableCollection<Kittens> _kittens;
然后我可以检索所有小猫并将来自 REST 调用的返回的 ICollection 转换为 ObservableCollection,如下所示:
_kittens = new ObservableCollection<Kitten>(await _client.KittenAllAsync());
酷,现在我有一个小猫对象的双向数据绑定集合,并且 UI 可以做任何事情。我还可以使用新的 ObservableCollection 注册所需的事件处理程序,以便响应集合中的添加/删除/移动事件,并发出相应的 REST 调用来更新远程数据存储上的记录。
我的问题是:
是否还可以接收有关任何给定小猫字段内的更改的事件,以便我还可以执行相应的 REST 调用来仅更新一只小猫,或者更好的是,仅更新该一只小猫的一个属性?
您还可以将WPF以MVVM的形式绑定到服务器。下面介绍如何以MVVM的形式进行绑定
假设您的服务器是一个
ASP.NET Core Web API
项目。在HomeController
中创建Controllers
,修改代码如下。
[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
// Data in the server to be synchronized with WPF
List<Kittens> list = new List<Kittens>() { new Kittens() { Id = "1",Name= "Kittens_1" },new Kittens() { Id = "2", Name = "Kittens_2" } };
[HttpGet("GetData")]
public List<Kittens> GetData()
{
return list;
}
[HttpPost("UpdateData")]
public string UpdateData(Kittens kittens)
{
//Find the data to be modified by Id in the List and modify it
return "Modified data"+kittens.Id + ":" + kittens.Name;
}
}
public class Kittens
{
public string Id { get; set; }
public string Name { get; set; }
}
在
Program.cs
中添加以下代码以允许跨域通信。然后WPF就可以通过HttpClient
与服务器通信了。
在下面添加以下代码
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(p =>
{
p.AddPolicy("MyCors", builder => {
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.AllowAnyOrigin();
});
});
在下面添加以下代码
var app = builder.Build();
app.UseCors(p =>
{
p.AllowAnyHeader();
p.AllowAnyMethod();
p.AllowAnyOrigin();
});
假设您的 WPF 项目是
WPF Application
。首先,您需要通过 CommunityToolkit.Mvvm
安装 Newtonsoft.Json
和 NuGet
。
创建
Model
文件夹并在Kittens
中创建
Model
public class Kittens:ObservableObject
{
public string id;
public string Id { get { return id; } set { id = value;OnPropertyChanged("Id"); } }
public string name;
public string Name { get { return name; } set { name = value;OnPropertyChanged("Name"); } }
}
创建一个
ViewModel
文件夹,并在 MainWindowViewModel
文件夹中创建 ViewModel
。
完整代码
public class MainWindowViewModel:ObservableObject
{
public ObservableCollection<Kittens> kittens { get; set; }
public RelayCommand<Kittens> saveCommand { get; set; }
private static readonly HttpClient client = new HttpClient();
private DispatcherTimer dispatcherTimer;
public MainWindowViewModel() {
kittens = new ObservableCollection<Kittens>();
saveCommand = new RelayCommand<Kittens>(SaveData);
LoadData();
InitializeTimer();
}
public async Task LoadData()
{
var response = await client.GetAsync("…/Home/GetData");
var responseString = await response.Content.ReadAsStringAsync();
var list = JsonConvert.DeserializeObject<List<Kittens>>(responseString);
kittens.Clear();
foreach (var item in list)
{
kittens.Add(item);
}
}
public void SaveData(Kittens kittens)
{
string json = JsonConvert.SerializeObject(kittens);
HttpContent content =new StringContent(json, Encoding.UTF8, "application/json");
client.PostAsync("…/Home/UpdateData", content);
}
private void InitializeTimer()
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = TimeSpan.FromSeconds(60*5); // Set the timer interval in seconds
dispatcherTimer.Tick += DispatcherTimer_Tick;
dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
// Perform timing operations here
LoadData();
}
}
在上面的代码中,
LoadData
用于从服务器获取数据,然后kittens
可以通过数据绑定将数据传递给DataGrid
。这里使用DispatcherTimer
中的InitializeTimer
定时器来间歇性地执行LoadData
,实现服务器到WPF的间歇性同步(间隔时间不要设置太小,因为一直发送网络请求非常消耗资源)
通过
Command
,当点击Save Edit
按钮时,执行SaveData
方法,向服务器发送网络请求,更新服务器中的数据,从而实现WPF到服务器的同步。
主窗口.xaml
xmlns:vm="clr-namespace:WpfApp.ViewModel"
<Window.Resources>
<vm:MainWindowViewModel x:Key="MainWindowViewModel"></vm:MainWindowViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource MainWindowViewModel}">
<DataGrid x:Name="MyDataGrid" ItemsSource="{Binding kittens}" IsReadOnly="False" AutoGenerateColumns="False" CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></DataGridTextColumn>
<DataGridTextColumn Header="Name" Binding="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></DataGridTextColumn>
<DataGridTemplateColumn Header="Option">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid DataContext="{StaticResource MainWindowViewModel}">
<Button Content="Save Edit" Command="{Binding saveCommand}"
CommandParameter="{Binding Path=CurrentItem,RelativeSource={RelativeSource AncestorType=DataGrid,AncestorLevel=1,Mode=FindAncestor}}"></Button>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>