Tel: 600283424
Wykresy w WPF C# – OxyPlot
PerfectSoft > Baza wiedzy > C# > Wykresy w WPF C# – OxyPlot

Wykresy w WPF C# – OxyPlot

EN

Wykresy w WPF C# – OxyPlot


Nieodłącznym elementem pracy na danych liczbowych jest ich analiza. A jaka forma interpretacji danych mogłaby być przyjemniejsza dla oka niż wykres zależności? Dlatego wpis dotyczył będzie narzędzia pozwalającego rysować wykresy w WPF C# – OxyPlot ułatwiającego to zadanie.


W projektach, które wymagają wyświetlenia kilku punktów w prosty sposób, spokojnie można to zrealizować za pomocą własnych klas. Czasem jednak ilość danych wzrasta oraz komplikuje się sposób wyświetlania np. poprzez skalę logarytmiczną, możliwość przybliżania fragmentu wykresu. W takich sytuacjach zaprogramowanie takiego narzędzia może zająć znaczną część czasu przeznaczonego na pierwotny projekt. Warto wtedy skorzystać z rozbudowanych narzędzi, które na pewno spełnią nasze wymagania.


Na rynku znajduje się wiele narzędzi wspomagających wyświetlanie wykresów np. XPlot, Chart, ZedGraph. Większość narzędzi napisana jest pod WinForms i tam znakomicie się spełniają. Co w sytuacji, gdy projekt od początku do końca projektowany jest w WPF? Można oczywiście połączyć projekt WPF z WinForms, ale istnieje narzędzie, które rozwiązuje ten problem.


OxyPlot jest biblioteką pozwalającą na bezpośrednią implementację w WPF, Windows 8, Windows Phone, Windows Phone Silverlight, Windows Forms, Silverlight, GTK#, Xwt, Xamarin.iOS, Xamarin.Android, Xamarin.Forms and Xamarin.Mac. Udostępniona jest na licencji MIT, która pozwala na swobodne korzystanie, dopóki w programach pochodnych zamieszczana jest treść licencji.



Od czego zacząć


Pracę z OxyPlot należy rozpocząć od dodania bibliotek do własnego projektu WPF. Dostępne są trzy sposoby dodania odpowiednich referencji:


1. Najłatwiejszym sposobem na to jest skorzystanie z nuget. Po uruchomieniu Package Manager Console należy wpisać:

  • Install-Package Oxyplot.Core
  • Install-Package Oxyplot.Wpf

2. Drugim sposobem jest wyszukanie bibliotek Oxyplot.Core oraz Oxyplot.Wpf poprzez interfejs nugetManage Nuget Packages w sposób zaprezentowany poniżej:

nuget - Wykresy w WPF C# - OxyPlot
nuget – Wykresy w WPF C# – OxyPlot

3. Trzeci sposób jest dla tych, którym nie działają poprzednie sposoby.


Z tego sposobu należy skorzystać jeżeli podczas instalacji wystąpił problem, nie działają pewne funkcje OxyPlot lub z jakiegoś powodu nie chcesz korzystać z nuget.


UWAGA
Zarówno poniższy opis, jak i przykłady podawane przez producenta odnoszą się do wersji 2013.1.38.1, niestety wersja obecnie znajdująca się w nuget to 2014.1.546 i może powodować to błędy. Jednym z nich jest:
The name „plot” does not exist in the namespace „http://oxyplot.codeplex.com”
Rozwiązaniem tego problemu jest dodanie referencji do starszej wersji plików, które można pobrać stąd: OxyPlot


Pobrany plik należy rozpakować, a następnie dodać referencję do nich w sposób przedstawiony poniżej.


1. W Solution Explorer należy znaleźć referencję, kliknąć prawym przyciskiem myszy i wybrać Add Reference.

ref1 - Wykresy w WPF C# - OxyPlot
Wykresy w WPF C# – OxyPlot

2. W nowo otwartym oknie należy kliknąć Browse i znaleźć pobrane pliki, następnie kliknąć Add.


ref2 - Wykresy w WPF C# - OxyPlot
Wykresy w WPF C# – OxyPlot

3. Ostatecznie należy sprawdzić czy są zaznaczone na liście i kliknąć OK.


ref3 - Wykresy w WPF C# - OxyPlot
Wykresy w WPF C# – OxyPlot

UWAGA
Dodanie dowolnego pliku przez nuget spowoduje aktualizację plików do nowszej wersji. W przypadku występowania wymienionego wcześniej błędu, pojawi się on ponownie.


Deklaracja kontrolki wykresu


Kontrolkę należy zadeklarować w kodzie XAML projektu. W nagłówku Window należy dopisać linię:


xmlns:oxy="http://oxyplot.codeplex.com"

Następnie w miejscu, w którym ma się pojawić wykres wpisać kod:


<oxy:Plot x:Name="Plot1" Title="A Graph" Model="{Binding PlotModel}" Margin="10" Grid.Row="1"> </oxy:Plot>

Parametrem wartym wyjaśnienia jest tu Model, do którego został ustawiony Binding. Jest to parametr, pozwalający na ustawienie wyglądu, przedziału, kolorów, serii danych itd.


OxyPlot pomimo funkcjonowania w WPF i bindowanego Model‚u nie wspiera w pełni modelu MVVM (Model View ViewModel). Oznacza to, że wyświetlenie zmian na wykresie wymaga uaktualnienia danych w obiekcie PlotModel w code-behind.


Implementacja obiektu PlotModel wykresu


Teraz, gdy w projekcie znajduje się już kontrolka, w której wyświetlany będzie wykres, należy zająć się jej wyglądem.


W modelu został ustawiony Binding, a więc konieczna jest deklaracja klasy z polem, którego zmiana będzie wywoływała reakcję poprzez akcesory. Taka klasa musi dziedziczyć z INotifyPropertyChanged i nadpisywać metodę OnPropertyChanged:


public class OxyPlotModel : INotifyPropertyChanged
{

  private OxyPlot.PlotModel plotModel;
  public OxyPlot.PlotModel PlotModel
  {
      get
      {
         return plotModel;
      }
      set
      {
         plotModel = value;
         OnPropertyChanged("PlotModel");
      }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected void OnPropertyChanged(string name)
  {
     PropertyChangedEventHandler handler = PropertyChanged;
     if (handler != null)
     {
        handler(this, new PropertyChangedEventArgs(name));
     }
  }
}

Obiekt tak zdefiniowanej klasy należy podpiąć pod interfejs z kontrolką wykresu.


//Deklaracja obiektu
private OxyPlotModel oxyPlotModel;

//W konstruktorze
oxyPlotModel = new OxyPlotModel();
this.DataContext = oxyPlotModel; // To pozwala połączyć kontrolki z polami klasy OxyPlotModel

Ustawianie parametrów wykresu


Teraz gdy wszystko jest już połączone ze sobą, pozostaje ustawienie odpowiednich parametrów polu PlotModel. W tym celu klasa OxyPlotModel zostanie rozbudowana. Sposób uzupełniania tych parametrów jest dowolny, ale w poniższym przykładzie zostało to rozdzielone na dwa etapy. Parametry, które się nie zmieniają uzupełniane są na początku, a te zależne od wykresu dopisywane są przed jego wyświetleniem. Stałymi parametrami są tu tylko te dotyczące legendy wykresu:


private void SetUpLegend()
{
  plotModel.LegendTitle = "Legenda";
  plotModel.LegendOrientation = OxyPlot.LegendOrientation.Horizontal;

  //Orientacja pozioma
  plotModel.LegendPlacement = OxyPlot.LegendPlacement.Outside; //Poza planszą wykresu
  plotModel.LegendPosition = OxyPlot.LegendPosition.TopRight; //Pozycja: góra, prawo
  plotModel.LegendBackground = OxyPlot.OxyColor.FromAColor(200, OxyPlot.OxyColors.White);//Tło białe
  plotModel.LegendBorder = OxyPlot.OxyColors.Black; //Ramka okna czarna
}

Nie zalecam wstępnej deklaracji osi wykresu, ponieważ mogą się wartości liczbowe z deklaracji i z samych danych wejściowych na siebie nachodzić i spowoduje to wizualny bałagan.


Drugą metodą klasy, wywoływaną zawsze gdy należy zmienić zawartość wykresu. Najprostszym przykładem pozwalającym narysować kosinusoidę jest metoda:


public void PodajDaneDoWykresu()
{
  this.MyModel = new PlotModel { Title = "Przykład 1" };
  this.MyModel.Series.Add(new FunctionSeries(Math.Cos, 0, 10, 0.1, "cos(x)"));
}

W przypadku wykresu na podstawie własnych punktów X i Y, ta sama klasa wyglądałaby tak:


public IList<OxyPlot.DataPoint> Points { get; private set; }
public void PodajDaneDoWykresu()
{
  this.plotModel.Title = "Przykład 2";
  this.Points = new List<OxyPlot.DataPoint>
  {
    new OxyPlot.DataPoint(5, 3),
    new OxyPlot.DataPoint(15, 17),
    new OxyPlot.DataPoint(25, 12),
    new OxyPlot.DataPoint(35, 4),
    new OxyPlot.DataPoint(45, 15),
    new OxyPlot.DataPoint(55, 10)
  };
}

lub:


OxyPlot.Series.LineSeries punktySerii;
public void PodajDaneDoWykresu()
{
  this.plotModel.Title = "Przykład 3";

  punktySerii.Points.Add(new OxyPlot.DataPoint(5, 3));
  punktySerii.Points.Add(new OxyPlot.DataPoint(15, 17));
  punktySerii.Points.Add(new OxyPlot.DataPoint(25, 12));
  punktySerii.Points.Add(new OxyPlot.DataPoint(35, 4));
  punktySerii.Points.Add(new OxyPlot.DataPoint(45, 15));
  punktySerii.Points.Add(new OxyPlot.DataPoint(55, 10));

  plotModel.Series.Add(punktySerii);
}

Dodatkowo w tej metodzie można uzupełnić informację dotyczące parametrów osi wykresu takie jak liniowe/logarytmiczne, zakres danych, wartość minimalna/maksymalna:


var xAxis = new OxyPlot.Axes.LinearAxis(OxyPlot.Axes.AxisPosition.Bottom, "a - offered traffic [Erl]")
{
  MajorGridlineStyle = OxyPlot.LineStyle.Solid,
  MinorGridlineStyle = OxyPlot.LineStyle.Dot
};
plotModel.Axes.Add(xAxis); var yAxis = new OxyPlot.Axes.LogarithmicAxis(OxyPlot.Axes.AxisPosition.Left, "Ei")
{
  MajorGridlineStyle = OxyPlot.LineStyle.Solid,
  MinorGridlineStyle = OxyPlot.LineStyle.Dot
};
plotModel.Axes.Add(yAxis);

Jeżeli serii danych jest więcej to warto by wyraźnie się różniły od siebie, dlatego należy zdefiniować parametry serii:


//deklaracja poza metodą
OxyPlot.Series.LineSeries punktySerii;

//implementacja w metodzie
punktySerii = new OxyPlot.Series.LineSeries
{
  MarkerType = OxyPlot.MarkerType.Plus, //oznaczenie punktów
  MarkerSize = 4, //wielkość punktów
  MarkerStroke = OxyPlot.OxyColors.Tomato, //Kolor linii wykresu
  Title = "Seria danych" //tytuł serii
};

Zbierając wszystkie omówione informacje, poniżej został zaprezentowany kod dla serii danych, podanych jako listy w parametrze:


public void PodajDaneDoWykresu(List<double> X, List<double> Y)//Lista X i Y podana jako parametr metody
{
  this.PlotModel = new OxyPlot.PlotModel();
  //Usunięcie ustawionych parametrów z poprzedniego uruchomienia metody
  plotModel.Series = new System.Collections.ObjectModel.Collection<OxyPlot.Series.Series> { };
  plotModel.Axes = new System.Collections.ObjectModel.Collection<OxyPlot.Axes.Axis> { };


  //Graficzne ustawienia wykresów
  punktySerii = new OxyPlot.Series.LineSeries
  {
    MarkerType = ksztaltPunktowWykresu[0], //oznaczenie punktów - definicja poniżej
    MarkerSize = 4, //wielkość punktów
    MarkerStroke = koloryWykresow[0], //Kolor linii wykresu - definicja poniżej
    Title = "Seria nr: " + (1).ToString() //tytuł serii
  };

  //Uzupełnianie danych
  for (int n = 0; n < X.Count; n++) 
    punktySerii.Points.Add(new OxyPlot.DataPoint(X[n], Y[n]));//dodanie wszystkich serii do wykresu

  plotModel.Series.Add(punktySerii);

  //Opis i parametry osi wykresu
  var xAxis = new OxyPlot.Axes.LinearAxis(OxyPlot.Axes.AxisPosition.Bottom, "a - offered traffic [Erl]")
  {
    MajorGridlineStyle = OxyPlot.LineStyle.Solid,
    MinorGridlineStyle = OxyPlot.LineStyle.Dot
  };
  plotModel.Axes.Add(xAxis);
  var yAxis = new OxyPlot.Axes.LogarithmicAxis(OxyPlot.Axes.AxisPosition.Left, "Ei")
  {
    MajorGridlineStyle = OxyPlot.LineStyle.Solid,
    MinorGridlineStyle = OxyPlot.LineStyle.Dot
  };
  plotModel.Axes.Add(yAxis);
}

W powyższym kodzie dodawane serie korzystały z kolejnych wartości tablic ksztaltPunktowWykresu i koloryWykresow. Są to listy z wypisanymi wartościami tych parametrów, po to by można było korzystać z nich w pętli. Przykładowa implementacja takich tablic znajduje się poniżej:


//Wypisane po to, by zmieniać kolor i kształt wraz z numerem klasy
private readonly List<OxyPlot.OxyColor> koloryWykresow = new List<OxyPlot.OxyColor>
{
  OxyPlot.OxyColors.Green,
  OxyPlot.OxyColors.IndianRed,
  OxyPlot.OxyColors.Coral,
  OxyPlot.OxyColors.Chartreuse,
  OxyPlot.OxyColors.Peru
};

private readonly List<OxyPlot.MarkerType> ksztaltPunktowWykresu = new List<OxyPlot.MarkerType>
{
  OxyPlot.MarkerType.Plus,
  OxyPlot.MarkerType.Star,
  OxyPlot.MarkerType.Cross,
  OxyPlot.MarkerType.Custom,
  OxyPlot.MarkerType.Square
};

Zapis wykresu do pliku


Gdy już się tak napracowaliśmy by wyświetlić wykres, to dobrze byłoby zachować go w formie pliku, do późniejszego odtworzenia. OxyPlot umożliwia skorzystania ze skrótu klawiszowego ctrl+c, by zapisać jego obraz w pamięci tymczasowej, następnie wystarczy go wkleić w dowolnym edytorze graficznym.


Wygodniejszą opcją jest jednak skorzystanie z metody, która pozwala na taki zapis do pliku:


OxyPlot.Wpf.PngExporter.Export(PlotModel, filename, 1600, 1600, OxyPlot.OxyColors.White, 96);

Parametry metody to kolejno:


  • Obiekt z modelem wykresu;
  • Nazwa pliku, wraz z lokalizacją i rozszerzeniem;
  • Szerokość w pikselach;
  • Wysokość w pikselach;
  • Kolor tła wykresu;
  • Liczba pikseli przypadająca na cal długości;

×
Czy życzą sobie Państwo kontaktu z naszej strony?
TOP