Skip to main content
 首页 » 编程设计

c#之Xamarin.Forms:如何从 Web API 数据填充饼图

2024年04月18日36dudu

大家好。我正在创建 Xamarin.Forms 可移植应用程序。我目前正在编写包含静态数据的饼图 (OxyPlot)。

我想做的是在我拥有的每个饼图切片中都有一个动态数据。意思是,数据应该来 self 的数据库。

我已经能够从我的数据库中检索数据并在我使用 Web Api 创建的移动应用程序中将其显示为 List ,如下所示:

ClientListPage.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" 
             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> 

ClientListPage.xaml.cs

using Newtonsoft.Json; 
using OxyPlot; 
using OxyPlot.Series; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Net.Http; 
using System.Net.Http.Headers; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Input; 
using Xamarin.Forms; 
using XamarinFormsDemo.Models; 
using XamarinFormsDemo.ViewModels; 
 
 
 
namespace XamarinFormsDemo.Views 
{ 
    public partial class ClientListPage : ContentPage 
    { 
 
        CustomerVM viewModel; 
        public ClientListPage() 
        { 
            NavigationPage.SetHasNavigationBar(this, true); 
 
 
            InitializeComponent(); 
            viewModel = new CustomerVM(); 
            BindingContext = viewModel; 
        } 
 
        async override protected void OnAppearing() 
        { 
            base.OnAppearing(); 
 
            var json = await GetCustomerAsync(); 
 
            var customers = JsonConvert.DeserializeObject<Customer[]>(json); 
 
            foreach (Customer c in customers) 
                viewModel.CustomerList.Add(c); 
        } 
 
        async Task<string> GetCustomerAsync() 
        { 
            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/Customer"); 
            if (response.IsSuccessStatusCode) 
            { 
                return await response.Content.ReadAsStringAsync(); 
            } 
            else return response.ReasonPhrase; 
        } 
 
 
    } 
} 

CustomerVM.cs

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Diagnostics; 
using System.Linq; 
using System.Net.Http; 
using System.Runtime.CompilerServices; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Input; 
using Xamarin.Forms; 
using XamarinFormsDemo.Models; 
using XamarinFormsDemo.Services; 
using XamarinFormsDemo.Views; 
 
 
namespace XamarinFormsDemo.ViewModels 
{ 
    public class CustomerVM : INotifyPropertyChanged 
    { 
 
 
        private ObservableCollection<Customer> _customerList; // keep all customers 
        private ObservableCollection<Customer> _searchedCustomerList; // keep a copy for searching 
        private Customer _selectedCustomer = new Customer(); 
 
        private string _keyword = ""; 
        public string Keyword 
        { 
            get 
            { 
                return _keyword; 
            } 
            set 
            { 
                this._keyword = value; 
 
                // while keyword changed we filter Employees 
                //Filter(); 
            } 
        } 
 
 
 
 
        public ObservableCollection<Customer> CustomerList 
        { 
            get 
            { 
                return _customerList; 
            } 
            set 
            { 
                _customerList = value; 
                OnPropertyChanged(); 
            } 
        } 
 
        public CustomerVM() 
        { 
            CustomerList = new ObservableCollection<Customer>(); 
        } 
 
 
 
        public event PropertyChangedEventHandler PropertyChanged; 
 
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
        { 
            var handler = PropertyChanged; 
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
        } 
 
    } 
} 

但这一次,我需要在饼图中进行。

我真的不知道该怎么做。但我认为我从上面的 WEB API 检索数据的方式与我将如何在图表中执行此操作有相似之处。希望你能帮助我。

非常感谢。这些是我的一些代码:

SalesPerProductViewModel.cs

using OxyPlot; 
using OxyPlot.Annotations; 
using OxyPlot.Axes; 
using OxyPlot.Series; 
using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Linq; 
using System.Runtime.CompilerServices; 
using System.Text; 
using System.Threading.Tasks; 
using XamarinFormsDemo.Models; 
 
namespace XamarinFormsDemo.ViewModels 
{ 
    public class SalesPerProductViewModel : INotifyPropertyChanged 
    { 
 
        private ObservableCollection<Sales> _salesList; // keep all customers 
 
 
        public ObservableCollection<Sales> SalesPerProductModel 
        { 
            get 
            { 
                return _salesList; 
            } 
            set 
            { 
                _salesList = value; 
                OnPropertyChanged(); 
            } 
        } 
 
        public SalesPerProductViewModel() 
        { 
 
            SalesPerProductModel = new ObservableCollection<Sales>(); 
 
 
        } 
 
 
 
 
        public event PropertyChangedEventHandler PropertyChanged; 
 
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 
        { 
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
        } 
 
 
 
    } 
} 

SalesPerProductPage.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:ViewModels="clr-namespace:XamarinFormsDemo.ViewModels;assembly=XamarinFormsDemo" 
             x:Class="XamarinFormsDemo.Views.SalesPerProductPage" 
             BackgroundImage="bg3.jpg" 
             Title="Sales Per Product"> 
 
 
  <ContentPage.BindingContext> 
    <ViewModels:SalesPerProductViewModel/> 
  </ContentPage.BindingContext> 
 
 
 
  <Label Text="TOTAL SALES LABEL HERE!" 
       TextColor="#24e97d"/> 
 
    <oxy:PlotView Model="{Binding SalesPerProductModel}" /> 
</ContentPage> 

SalesPerProductPage.xaml.cs

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; 
        } 
 
 
    } 
} 

Sales.cs

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; } 
    } 
} 

请您参考如下方法:

您正在将您的 PlotModel 分配给局部变量。您必须将其分配给您的 ViewModel。这是您重构的工作代码:

SalesPerProductPage.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> 

SalesPerProductPage.xaml.cs:

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; 
    } 
} 

SalesPerProductViewModel:

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() 
    { 
    } 
}