В WPF существует множество решений для большинства проблем. Я обсужу три возможных решения вашей проблемы - тот, который вы описали, и два других. Вы можете решить, какой из них будет лучше всего работать.
Решение 1: Использование объектов TextBlock для Disply этикетки
Это звучит, как вы есть Canvas
и вы добавляете TextBlock
к нему для каждой отметки. Это жизнеспособное решение, если производительность не слишком критична, и вы не можете использовать привязку данных.
Есть два пути устранения TextBlocks в этом случае:
Вы можете оставить List<TextBlock>
, содержащие весь список TextBlocks из TextBlocks созданных в последний раз вы создали метки. Всякий раз, когда вы воссоздаете ярлыки, пропустите этот список и удалите каждый текстовый блок в списке с панели-заполнителя (Canvas)
Вы можете создать новый холст и поместить на него текстовые блоки, а затем удалить весь холст, когда вы переименуйте.
Вот пример второго способа, так как это немного более эффективным:
class MyGraphBuilder
{
Canvas _labelCanvas;
...
void AddLabels()
{
// Remove old label canvas, if any
if(_labelCanvas!=null)
_graphCanvas.Children.Remove(_labelCanvas);
// Add new label canvas
_labelCanvas = new Canvas();
_graphCanvas.Children.Add(_labelCanvas);
// Create labels
foreach(...)
{
...
_labelCanvas.Add(new TextBlock ...
}
...
}
}
Решение 2: Использование связывания данных
В WPF можно создавать множество графиков без написав одну строку кода! WPF встроенный в связывании данных достаточно для создания относительно сложных гистограмм и т.д.
Вот пример использования привязки, чтобы создать простую гистограмму данных:
<ItemsControl ItemsSource="{Binding myData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Width="50" Text="{Binding Label}"/>
<Rectangle VerticalAlignment="{Stretch}" Width="{Binding Value}">
<Rectangle.LayoutTransform>
<ScaleTransform ScaleX="10" /> <!-- Scale factor here, can be binding too -->
</Rectangle.LayoutTransform>
</Rectangle>
<TextBlock Text="{Binding Value}" FontSize="8"/>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Цифровые метки могут быть добавлены к горизонтали оси, используя второй элемент ItemsControl, выложенный горизонтально, и с его шаблоном данных фиксированная ширина и показывающие числа и отметки.
Решение 3: Использование низкого уровня рисования классов
построить свой график, создав DrawingGroup
объекта и добавление GeometryDrawing
и GlyphRunDrawing
объектов к нему, а затем положить DrawingGroup
внутри DrawingVisual
и добавить, что к вашему основным Panel
.
Вы должны использовать один GeometryDrawing
или GlyphRunDrawing
для каждого набора предметов, совместно использующих кисть и ручку. Например, если ваши оси и отметки имеют одинаковый цвет и ширину, создайте один из них GeometryDrawing
для всех из них, но если каждая отметка имеет цвет differnet, создайте несколько объектов GeometryDrawing
.
Вы создадите объект Geometry
для каждого GeometryDrawing
. Для лучшей эффективности он должен быть StreamGeometry
, но другие классы Geometry также хорошо работают, могут быть проще в использовании и могут быть инициализированы в XAML. Создание PathGeometry
или EllipseGeometry
, вероятно, уже знакомо с вами, поэтому я сосредоточусь на создании StreamGeometry
. Вы делаете это, вызывая метод Open
в операторе using()
, а затем записывая его в возвращаемый контекст. Вот пример:
Geometry BuildAxesAndTicksGeometry()
{
// First create geometry
var geometry = new StreamGeometry();
using(var context = geometry.Open())
{
// Horizontal axis
context.BeginFigure(new Point(0,0), false, false);
context.LineTo(new Point(_width, 0), true, false);
// Vertical axis
context.BeginFigure(new Point(0,0), false, false);
context.LineTo(new Point(0, _height), true, false);
// Horizontal ticks
for(int i=0; i<_nTicksHorizontal; i++)
{
context.BeginFiture(new Point(i * _tickSpacing, -10), false, false);
context.LineTo(new Point(i * _tickSpacing, 10), true, false);
}
// Do same for vertical ticks
}
// Now add it to a drawing
return new GeometryDrawing { Geometry = geometry, Stroke = _axisPen };
}
Drawing BuildDrawing()
{
var mainDrawing = new DrawingGroup();
mainDrawing.Add(BuildAxesAndTicksGeometry());
... // Add other drawings, including one or more for the data
return mainDrawing;
}
void UpdateDrawing()
{
myDrawingVisual.Drawing = BuildDrawing(); // where myDrawingVisual is defined in the XAML
}
Сравнение решений
В большинстве случаев я бы рекомендовал раствор 2 или 3, по этим причинам:
- Если граф достаточно прост в использовании привязка данных, это сэкономит вам много времени. Перейдите с решением 2.
- Если график не может быть выполнен с привязкой данных, использование объектов чертежа примерно так же просто, как и любая другая техника, и может работать лучше. Перейдите с решением 3.
В вашем случае, если вы уже вложили значительную работу в свое решение 1, вы можете захотеть придерживаться его, хотя это, вероятно, не самое лучшее.