Для проекта, над которым мы работаем, нам нужен простой (и бесплатный) пользовательский контроль WP7-графа. Данные, которые визуализирует пользовательский контроль, должны быть привязаны к данным, и каждый раз, когда поступают новые данные, график должен обновляться на лету. В следующем уроке мы покажем, как создать такой элемент управления с нуля (обратите внимание, что обычно это также должен работать в проектах Silverlight или WPF).
Демо-решение этого урока можно скачать здесь .
 
График Пользовательский контроль: Xaml-код
Сначала мы создаем новый UserControl. Часть xaml довольно пуста, мы добавим две вещи:
- Стиль-ресурс, который мы будем использовать при рисовании графика. В этом примере мы хотим, чтобы графические линии имели тот же цвет, что и цвет акцента телефона.
 - Пустой 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 состоит из двух важных частей:
- Метод DrawGraph (), который будет … ждать его … рисовать график внутри gridcontrol
 - Свойство зависимости (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));
}
И это упаковка!
