C# 表单:如何从WebAPI数据填充饼图?
大家好。我正在创建一个Xamarin.Forms可移植应用程序。我目前正在编写一个包含静态数据的PieChart(OxyPlot) 我想做的是在我拥有的每个饼图切片中都有一个动态数据。也就是说,数据应该来自我的数据库 我已经能够从数据库中检索数据,并在我使用Web Api创建的移动应用程序中以列表的形式显示数据,如下所示: ClientListPage.xamlC# 表单:如何从WebAPI数据填充饼图?,c#,asp.net-web-api,xamarin,charts,xamarin.forms,C#,Asp.net Web Api,Xamarin,Charts,Xamarin.forms,大家好。我正在创建一个Xamarin.Forms可移植应用程序。我目前正在编写一个包含静态数据的PieChart(OxyPlot) 我想做的是在我拥有的每个饼图切片中都有一个动态数据。也就是说,数据应该来自我的数据库 我已经能够从数据库中检索数据,并在我使用Web Api创建的移动应用程序中以列表的形式显示数据,如下所示: ClientListPage.xaml <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns
<?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="XamarinFormsDemo.Views.ClientListPage"
xmlns:ViewModels="clr-namespace:XamarinFormsDemo.ViewModels;assembly=XamarinFormsDemo"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
BackgroundImage="bg3.jpg"
Title="Client List1">
<StackLayout>
<SearchBar Placeholder="Search" Text="{Binding Keyword}" SearchCommand="{Binding SearchCommand}" x:Name="txtSearch" />
<ListView ItemsSource="{Binding CustomerList, Mode=TwoWay}"
HasUnevenRows="True"
IsPullToRefreshEnabled="True"
x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage Source="icon.png"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2"
/>
<Label Grid.Column="1"
Text="{Binding CUSTOMER_NAME}"
TextColor="#24e97d"
FontSize="24"/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding CUSTOMER_CODE}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
<Label Grid.Column="1"
Grid.Row="2"
Text="{Binding CUSTOMER_MOBILE_NUMBER}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
<Label Grid.Column="1"
Grid.Row="3"
Text="{Binding CUSTOMER_EMAIL_ADDRESS}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
当您解析json并填充列表以输入listview时,您可以在viewmodel中填充该系列。一旦你有了客户对象的列表,浏览它们并根据需要使用它们的数据来创建饼图切片 以下几行中的某一行:
var json = await GetCustomerAsync();
var customers = JsonConvert.DeserializeObject<Customer[]>(json);
dynamic seriesP1 = new PieSeries { StrokeThickness = 2.0, InsideLabelPosition = 0.8, AngleSpan = 360, StartAngle = 0 };
foreach (Customer c in customers) {
seriesP1.Slices.Add(new PieSlice(c.CustomerName, c.SomeValue) { IsExploded = false, Fill = OxyColors.Teal });
}
var json=await GetCustomerAsync();
var customers=JsonConvert.DeserializeObject(json);
动态系列P1=新系列{StrokeThickness=2.0,InsideLabelPosition=0.8,AngleSpan=360,StartAngle=0};
foreach(客户中的客户c){
seriesP1.Slices.Add(新的PieSlice(c.CustomerName,c.SomeValue){isexploed=false,Fill=OxyColors.Teal});
}
我不知道你的客户类别是什么样的,你想展示什么样的价值观,等等。所以请记住这一点。这只是我的意思的一个例子。
另外,构造代码,使您只进行一次Http调用,然后将该客户列表用于列表和图表,不要下载两次数据,只是为了以两种不同的方式显示数据。您正在将
PlotModel
分配给一个局部变量。您必须将其分配给您的ViewModel
。以下是重构后的工作代码:
salerproductpage.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:oxy="clr-namespace:OxyPlot.Xamarin.Forms;assembly=OxyPlot.Xamarin.Forms"
xmlns:local="clr-namespace:App1"
x:Class="App1.SalesPerProductPage">
<ContentPage.Content>
<oxy:PlotView Model="{Binding SalesPerProductModel}"></oxy:PlotView>
</ContentPage.Content>
</ContentPage>
public partial class SalesPerProductPage : ContentPage
{
public SalesPerProductViewModel viewModelforSales { get; set; }
public SalesPerProductPage()
{
InitializeComponent();
viewModelforSales = new SalesPerProductViewModel();
BindingContext = viewModelforSales;
}
async protected override void OnAppearing()
{
base.OnAppearing();
var json = await GetSalesPerProductAsync();
var salesPerProduct = JsonConvert.DeserializeObject<Sales[]>(json);
PlotModel modelForSales = new PlotModel
{
Title = "Sales Per Product",
TitleColor = OxyColors.Teal,
TitleFontSize = 30,
TextColor = OxyColors.White,
DefaultFont = "Arial Black",
DefaultFontSize = 20
};
dynamic seriesP2 = new PieSeries { StrokeThickness = 2.0, InsideLabelPosition = 0.8, AngleSpan = 360, StartAngle = 0, FontSize = 24 };
foreach (Sales c in salesPerProduct)
{
seriesP2.Slices.Add(new PieSlice(c.PRODUCT_CODE, c.PRODUCT_ID));
}
modelForSales.Series.Add(seriesP2);
viewModelforSales.SalesPerProductModel = modelForSales;
}
async Task<string> GetSalesPerProductAsync()
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://10.0.0.17:64550/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/Sales");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else return response.ReasonPhrase;
}
}
public class SalesPerProductViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private PlotModel _salesPerProductModel;
public PlotModel SalesPerProductModel
{
get
{
return _salesPerProductModel;
}
set
{
if (value != _salesPerProductModel)
{
_salesPerProductModel = value;
PropertyChanged(this, new PropertyChangedEventArgs("SalesPerProductModel"));
}
}
}
public SalesPerProductViewModel()
{
}
}
谢谢您的回答,先生。我的问题不在于客户,而在于销售。你所说的“下载两次数据只是为了以两种不同的方式显示”是什么意思?我只是在ClientListPage.xaml.cs中下载一次数据,然后对销售数据执行同样的操作。一旦你下载了用于销售的json并反序列化到一个对象,然后旋转它并为你的图表加载PieSicle对象。先生,我应该把这个放在哪里?foreach(customers中的Customer c){seriesP1.Slices.Add(new PieSlice(c.CustomerName,c.SomeValue){IsExploded=false,Fill=OxyColors.Teal})}我已经为salerproductpage.xaml.cs添加了代码问题是,在
salerproductpage.xaml.cs
中,创建modelForSales
,然后将其分配给本地this.salerproductmodel
变量,而不是分配给viewModelforSales.salerproductmodel
。此外,为了做到这一点,您必须将Sales.SalesPerProductModel的视图模型从ObservableCollection
更改为PlotModel
。还有一件事:出于测试目的,您的XAML中有PlotView
本身。@jstreet先生,我如何将ObservableCollection转换为PlotModel?声明SalesPerProductModel
为PlotModel
,而不是ObservableCollection
。您必须仅在Portable
项目中执行此操作,而不是像以前那样在Web API中执行此操作。@jstreet先生,我是这样做的。就在OnAppearing方法下面。我做得对吗?不,你在改变一个局部变量。。。。您必须更改销售的model.salerproductmodel
。先生,我的应用程序中只显示了salerproduct标题?请从XAML中删除任何标签
,只保留绘图视图
,就像我发布的XAML一样。此外,使用调试器验证您是否使用有效数据正确添加了饼图切片。先生,我设置了一些断点,发现我的产品ID返回的值为零值,这就是为什么我无法创建切片。您必须检查的第一件事是OnAppearing()
中的var json
的内容。
using OxyPlot;
using OxyPlot.Xamarin.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XamarinFormsDemo.ViewModels;
using Xamarin.Forms;
using XamarinFormsDemo.Models;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using OxyPlot.Series;
namespace XamarinFormsDemo.Views
{
public partial class SalesPerProductPage : ContentPage
{
private PlotModel modelForSales;
SalesPerProductViewModel viewModelforSales;
public SalesPerProductPage()
{
InitializeComponent();
viewModelforSales = new SalesPerProductViewModel();
BindingContext = viewModelforSales;
} //end of SalesPerProductPage()
async override protected void OnAppearing()
{
base.OnAppearing();
var json = await GetSalesPerProductAsync();
var salesPerProduct = JsonConvert.DeserializeObject<Sales[]>(json);
modelForSales = new PlotModel
{
Title = "Sales Per Product",
TitleColor = OxyColors.Teal,
TitleFontSize = 30,
TextColor = OxyColors.White,
DefaultFont = "Arial Black",
DefaultFontSize = 20
};
dynamic seriesP2 = new PieSeries { StrokeThickness = 2.0, InsideLabelPosition = 0.8, AngleSpan = 360, StartAngle = 0 };
foreach (Sales c in salesPerProduct)
{
seriesP2.Slices.Add(new PieSlice(c.PRODUCT_CODE, c.PRODUCT_ID) { IsExploded = false, Fill = OxyColors.Teal });
}
modelForSales.Series.Add(seriesP2);
this.SalesPerProductModel = modelForSales;
}
public PlotModel SalesPerProductModel { get; private set; }
async Task<string> GetSalesPerProductAsync()
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://192.168.1.11:50857/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/Sales");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else return response.ReasonPhrase;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace XamarinFormsDemo.Models
{
public class Sales
{
public int Id { get; set; }
public int ORDER_ID { get; set; }
public int ORDER_DETAILS_ID { get; set; }
public int PRODUCT_ID { get; set; }
public string PRODUCT_CODE { get; set; }
public string NET_AMOUNT { get; set; }
}
}
var json = await GetCustomerAsync();
var customers = JsonConvert.DeserializeObject<Customer[]>(json);
dynamic seriesP1 = new PieSeries { StrokeThickness = 2.0, InsideLabelPosition = 0.8, AngleSpan = 360, StartAngle = 0 };
foreach (Customer c in customers) {
seriesP1.Slices.Add(new PieSlice(c.CustomerName, c.SomeValue) { IsExploded = false, Fill = OxyColors.Teal });
}
<?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:oxy="clr-namespace:OxyPlot.Xamarin.Forms;assembly=OxyPlot.Xamarin.Forms"
xmlns:local="clr-namespace:App1"
x:Class="App1.SalesPerProductPage">
<ContentPage.Content>
<oxy:PlotView Model="{Binding SalesPerProductModel}"></oxy:PlotView>
</ContentPage.Content>
</ContentPage>
public partial class SalesPerProductPage : ContentPage
{
public SalesPerProductViewModel viewModelforSales { get; set; }
public SalesPerProductPage()
{
InitializeComponent();
viewModelforSales = new SalesPerProductViewModel();
BindingContext = viewModelforSales;
}
async protected override void OnAppearing()
{
base.OnAppearing();
var json = await GetSalesPerProductAsync();
var salesPerProduct = JsonConvert.DeserializeObject<Sales[]>(json);
PlotModel modelForSales = new PlotModel
{
Title = "Sales Per Product",
TitleColor = OxyColors.Teal,
TitleFontSize = 30,
TextColor = OxyColors.White,
DefaultFont = "Arial Black",
DefaultFontSize = 20
};
dynamic seriesP2 = new PieSeries { StrokeThickness = 2.0, InsideLabelPosition = 0.8, AngleSpan = 360, StartAngle = 0, FontSize = 24 };
foreach (Sales c in salesPerProduct)
{
seriesP2.Slices.Add(new PieSlice(c.PRODUCT_CODE, c.PRODUCT_ID));
}
modelForSales.Series.Add(seriesP2);
viewModelforSales.SalesPerProductModel = modelForSales;
}
async Task<string> GetSalesPerProductAsync()
{
var client = new HttpClient();
client.BaseAddress = new Uri("http://10.0.0.17:64550/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/Sales");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else return response.ReasonPhrase;
}
}
public class SalesPerProductViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private PlotModel _salesPerProductModel;
public PlotModel SalesPerProductModel
{
get
{
return _salesPerProductModel;
}
set
{
if (value != _salesPerProductModel)
{
_salesPerProductModel = value;
PropertyChanged(this, new PropertyChangedEventArgs("SalesPerProductModel"));
}
}
}
public SalesPerProductViewModel()
{
}
}