Для проекта, над которым мы работаем, нам нужен простой (и бесплатный) пользовательский контроль 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));
}
И это упаковка!
