是否可以直接在 XAML 中与 Visual Studio REST 客户端服务代码生成器生成的对象进行双向数据绑定?

问题描述 投票:0回答:1

是否可以或实用将 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 调用来仅更新一只小猫,或者更好的是,仅更新该一只小猫的一个属性?

c# xaml mvvm data-binding visual-studio-2022
1个回答
0
投票

您还可以将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>
© www.soinside.com 2019 - 2024. All rights reserved.