Xamarin.Forms: How To Populate A Pie Chart From We

2019-05-31 01:10发布

Good Day Everyone. I'm creating a Xamarin.Forms Portable Application. And I'm currently coding a PieChart (OxyPlot) that contains static data.

What I want to do is to have a Dynamic Data in every Pie Slice I have. Meaning, the data should come from my database.

I'm already able to retrieved data from my database and display it as a List in the Mobile Application that I'm creating using Web Api like this one :

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

    }
}

But this time, I need to do it in a PieChart.

I really don't have an idea how to do this. But I think the way I retrieved the data from my WEB API above has similarity on how am I going to do this in my chart. Hope you can help me.

Thanks a lot. These are some of my codes :

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 (This is where the dynamic chart should be displayed)

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

2条回答
对你真心纯属浪费
2楼-- · 2019-05-31 01:22

When you parsed your json and populated the list to feed into the listview, you can do the same for populating the series in your viewmodel. Once you have a list of Customer objects, go through them and use their data to create the pie slices as necesarry.

Something among the lines of:

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

I do not know what your Customer class looks like, what values you want to show, etc. So keep that in mind. This is just an example of what I mean. Also, structure your code so you only make the Http call once, then use that list of customers for both the list and the chart, don't download the data twice just to show it in two different ways.

查看更多
一纸荒年 Trace。
3楼-- · 2019-05-31 01:46

You're assigning your PlotModel to a local variable. You must assign it to your ViewModel. Here's your refactored working code:

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

enter image description here

查看更多
登录 后发表回答