Статьи

Управление графиком с привязкой к данным WP7

Для проекта, над которым мы работаем, нам нужен простой (и бесплатный) пользовательский контроль WP7-графа. Данные, которые визуализирует пользовательский контроль, должны быть привязаны к данным, и каждый раз, когда поступают новые данные, график должен обновляться на лету. В следующем уроке мы покажем, как создать такой элемент управления с нуля (обратите внимание, что обычно это также должен работать в проектах Silverlight или WPF).

Демо-решение этого урока можно скачать здесь .

График Пользовательский контроль: Xaml-код

Сначала мы создаем новый UserControl. Часть xaml довольно пуста, мы добавим две вещи:

  1. Стиль-ресурс, который мы будем использовать при рисовании графика. В этом примере мы хотим, чтобы графические линии имели тот же цвет, что и цвет акцента телефона.
  2. Пустой gridcontrol, в котором будет нарисован график
<UserControl x:Class="GraphControlDemo.GraphControl">

    <UserControl.Resources>
        <Style TargetType="Polyline" x:Key="graphLine">
            <Setter Property="Stroke" Value=
                    "{StaticResource PhoneAccentColor}" />
            <Setter Property="StrokeThickness" Value="2" />
        </Style>
    </UserControl.Resources>

    <Grid x:Name="GraphGrid"  />

</UserControl>

Если вы хотите более необычный пользовательский контроль, вы можете добавить красивую рамку или любой другой материал, который вам нравится. Пока где-то есть gridcontrol с именем «GraphGrid», все будет хорошо.

График Пользовательский контроль: код позади, обзор

Кодовая часть нашего usercontrol состоит из двух важных частей:

  1. Метод DrawGraph (), который будет … ждать его … рисовать график внутри gridcontrol
  2. Свойство зависимости (DP), содержащее коллекцию значений (целых), из которых мы хотим построить график.

 

public partial class GraphControl : UserControl
    {
        public GraphControl()
        {
            InitializeComponent();
            Loaded += (s,e) => DrawGraph();
        }


        public void DrawGraph()
        {
            //...           
        }

       // GraphValue DependencyProperty
    }

Также обратите внимание, что мы явно вызываем метод DrawGraph () после загрузки usercontrol (строка 6). Если мы этого не сделаем, наш график будет отрисован с самого начала, даже если свойство зависимостей уже имеет привязанную коллекцию.

График Пользовательский контроль: код позади, рисование графика

Чтобы нарисовать реальный график, мы скопируем и отредактируем очень простое решение с помощью «stoneTip», который ответит на этот вопрос StackOverflow . Обязательно ознакомьтесь с исходным кодом в StackOverflow, если вам нужна дополнительная информация о том, что на самом деле происходит.

Мы разделяем метод DrawGraph () на две части: одна, которая будет рисовать график, другая часть используется, если к элементу управления не привязаны никакие данные, которые затем просто покажут «Нет данных» внутри элемента управления:

public void DrawGraph()
{
    
    if (GraphValues != null && GraphValues.Count(i => true) > 0)
    {
        //Draw graph lines
    }
    else
    {
        GraphGrid.Children.Clear();
        GraphGrid.Children.Add(new TextBlock()
                                   {
                                       Text = "No data", 
                                       VerticalAlignment = VerticalAlignment.Center, 
                                       HorizontalAlignment = HorizontalAlignment.Center
                                   });
    }
}

Фактическая часть чертежа (внутри if) выглядит следующим образом:

int maxGraphVal = GraphValues.Max(p => p);
int minGraphVal = GraphValues.Min(p => p);
int gridUnit = 
    (int)GraphGrid.ActualWidth / GraphValues.Count(i => true);
int graphHeight = 
    (int)GraphGrid.ActualHeight;
                
               
var sparkLine = new Polyline()
                    {
                        Style = (Style)Resources["graphLine"]
                    };

//Process each value and compute place in graph
int currentX = 0;
decimal graphValRange = maxGraphVal - minGraphVal;
foreach (int graphVal in GraphValues)
{
                   
    decimal graphY2Val = 
        (graphVal - minGraphVal) / graphValRange;
                    
    double graphY2ValDouble = 
        Convert.ToDouble(graphHeight - (graphY2Val * graphHeight));
    
                    
    sparkLine.Points.Add(new Point(currentX, graphY2ValDouble));
    currentX += gridUnit;
}

// Add the spark line to the graph
GraphGrid.Children.Clear();
GraphGrid.Children.Add(sparkLine);

Пользовательский элемент управления графиком: выделение кода, добавление свойства зависимости данных источника

Наконец, нам нужно определить свойство зависимости, с которым пользователи нашего элемента управления могут связывать точки графа. Для этого мы будем использовать ObservableCollection (хотя я совершенно уверен, что это не самое легкое решение):

public ObservableCollection<int> GraphValues
{
    get { return (ObservableCollection<int>)GetValue(GraphDataProperty); }
    set { SetValue(GraphDataProperty, value); DrawGraph(); }
}


public static readonly DependencyProperty GraphDataProperty = 
    DependencyProperty.Register("GraphValues",
        typeof(ObservableCollection<int>),
        typeof(GraphControl),
        new PropertyMetadata(null, OnGraphValuesChanged));

 Чего сейчас не хватает, так это части, которая будет автоматически обновлять наш график каждый раз, когда новые значения добавляются в коллекцию или изменения привязки. Эта часть вдохновлена ​​решением «Джош Дж», который отвечает на этот вопрос .

private static void OnGraphValuesChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    
    GraphControl me = d as GraphControl;
    if (me != null)
    { 
        var old = e.OldValue as ObservableCollection<int>;
        if (old != null)
            old.CollectionChanged -= me.OnGraphValueChanged;

        var n = e.NewValue as ObservableCollection<int>;
        if (n != null)
            n.CollectionChanged +=  me.OnGraphValueChanged ;
    }
}


private void OnGraphValueChanged
    (object sender, NotifyCollectionChangedEventArgs e)
{
    DrawGraph();
}

Использование элемента управления

С UserControl готов и ждет. Теперь мы можем легко добавить наш новый «GraphControl» где угодно.

Сначала добавьте пространство имен на страницу, на которую мы хотим добавить элемент управления:

xmlns:myGraph="clr-namespace:GraphControlDemo" 

Затем добавьте элемент управления:

<myGraph:GraphControl   GraphValues="{Binding SampleData}"/> 

В выделенном фрагменте кода теперь нам нужно только определить некоторую ObservableCollectioncalled SampleData, которая содержит точки, которые будут нарисованы на графике. Например:

public ObservableCollection<int> SampleData { get; set; }

public MainPage()
{
    InitializeComponent();

    SampleData = new ObservableCollection<int>() { 1, 2, 4 };
    
    LayoutRoot.DataContext = this;
} 

 Проверить, работает ли автообновление. Вы можете добавить кнопку, которая добавляет случайную выборку в коллекцию при каждом нажатии:

private void AddRandSampleBtnClick(object sender, RoutedEventArgs e)
{
    Random r= new Random();
    SampleData.Add(r.Next(-10, 50));
}

 И это упаковка!