#wpf #canvas #itemscontrol #pathgeometry
#wpf #холст #itemscontrol #pathgeometry
Вопрос:
У меня есть ItemsControl
с a Canvas
как его ItemsPanel
и Path
как ItemTemplate
. Цель состоит в том, чтобы построить график, поэтому Path.Data
он должен содержать геометрию, которая будет нарисована на холсте с абсолютными координатами.
Если я создаю экземпляр Canvas и помещаю путь непосредственно в него, он работает нормально.
Но если я использую ItemsControl, каждый путь заканчивается внутри ContentPresenter, а затем координаты теряются, поскольку ContentPresenter выравнивается по началу координат Canvas.
Вот мой код:
<ItemsControl
ItemsSource="{Binding Signals}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Path
Stroke="Red"
StrokeThickness="1"
StrokeDashCap="Round"
StrokeLineJoin="Round"
Stretch="Fill"
Data={Binding Converter=SignalToGeometryConverter}
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Комментарии:
1. Попробуйте удалить
Stretch="Fill"
из вашегоPath
2. Это также работает для меня, если я восстановлю Stretch=»Fill» и добавлю ко всем строкам геометрии пути префикс
"M 0,0"
—, за которым следует"M x,y"
для любогоx,y
места, где я действительно хочу, чтобы путь начинался. Если я"M -100,-100"
и растягиваю, это смещает все, что нужно-100,-100
отобразить. Каждый раз, когда я используюStretch
атрибут onPath
, я в конечном итоге знаю меньше, чем знал заранее.
Ответ №1:
Если вам действительно нужно использовать a Canvas
для ItemsPanel
этого ItemsControl
, и вам действительно нужно заполнить родительский элемент, вы можете оказаться в затруднительном положении. Если вы можете обойтись с помощью a Grid
для ItemsPanel
, у вас все готово.
То, что вас сюда приводит, это не то ContentPresenter
, это Stretch
. Это приводит Path
к заполнению его родительского элемента — но не так, как вы думаете. "M 100,100 L 200,200"
, с любым Stretch
другим, чем None
, нарисует линию, начинающуюся в верхнем левом углу родительского элемента. Ограничивающий прямоугольник, который он собирается использовать, — это максимальное и минимальное значения x и y, которые фактически использует геометрия пути. Предполагается, что вас интересуют только те области, в которые вы потрудились зайти. Если вы накладываете несколько путей, которые имеют разные верхние левые (и нижние правые) границы, все они будут масштабироваться и смещаться по-разному, создавая общий хэш всего.
Когда вы думаете об этом, даже если растяжение начиналось с 0,0 независимо от того, как оно узнает, что использовать для нижних правых границ? Пути не знают друг о друге. Если бы у вас был один путь, и вы добавили к нему каждую из ваших текущих вещей как другую фигуру, это было бы одно. Но таким образом, нет общей системы отсчета, о которой они знают.
Итак, самое быстрое решение — удалить Stretch="Fill"
, но тогда ничего не будет растягиваться. И пока вы используете Canvas
for ItemsPanel
, вы можете также не пытаться растягивать пути, потому что (насколько я могу судить по тестированию) в этом случае они все равно не будут растягиваться.
Если вы хотите растянуть, сначала вам нужно, чтобы все ваши пути имели одинаковую ограничивающую рамку, независимо от того, используют они все целиком или нет. Это означает, что сначала нужно вычислить, сколько горизонтального и вертикального пространства вам понадобится, и добавить к данным каждого пути префикс
M 0,0 M xmax,ymax
…перед переходом к фактической точке вы хотите запустить Path
at .
А затем измените ваше Canvas
значение на a Grid
.
Вот код, который я тестировал с:
XAML
<Grid>
<ItemsControl
ItemsSource="{Binding Signals, RelativeSource={RelativeSource AncestorType=Window}}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Path
Stroke="DeepSkyBlue"
StrokeThickness="1"
Data="{Binding}"
Stretch="Fill"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
int left = 10;
int top = 100;
int bottom = 200;
var signals =
Enumerable.Range(0, 19)
.Select(n => $"M 0,0 M 300,300 M {(n * 10) left},{top} L {(n * 15) left},{bottom}")
.ToList();
// Show shared bounding box
signals.Add("M 0,0 L 300,0 L 300,300 L 0,300 Z");
Signals = signals;
}
public IList Signals
{
get { return (IList)GetValue(SignalsProperty); }
set { SetValue(SignalsProperty, value); }
}
public static readonly DependencyProperty SignalsProperty =
DependencyProperty.Register("Signals", typeof(IList), typeof(MainWindow),
new PropertyMetadata(null));
}
Комментарии:
1. Большое вам спасибо за ваш подробный ответ! На самом деле у меня не было намерения растягивать-заполнять что-либо — значение заполнения было остатком от предыдущих попыток проектирования. Это должно было быть удалено давно, я только забыл, и это было несколько скрыто в подробностях всего. Просто удаление этого заставило все работать, но в любом случае ваши дальнейшие разработки были поучительными! Еще раз спасибо!
2. @heltonbiker Всегда пожалуйста. Это был забавный вопрос. Я сам многому научился!
3. В качестве примечания, я был довольно увлечен «построением графиков» с использованием WPF. Я получаю наилучшие результаты, предоставляя геометрии с глобальными преобразованиями к фигурам, которые я рисую, и обычно эти фигуры имеют
Path
тип, с различными типами геометрии (RectangleGeometry, PathGeometry и т. Д.). Таким образом, я могу применить что-то вроде «ViewPort» к холсту и получить приятные эффекты масштабирования простопутем изменения (привязки к данным) Преобразование.4. Это создает хорошую независимость между координатами «модели» (x = время, y = интенсивность, в моем домене приложения) и координатами «просмотра» (пикселями), так что масштабирование может быть действием только для просмотра, MVVM-wise, выполняемым обработчиками событий в коде.
5. @heltonbiker Я люблю преобразования. Я предполагал, что у вас откуда-то поступали данные, и хотел рассматривать их как непрозрачные: просто поместите это так, как оно говорит. Но, конечно, если вы явно задаете ограничивающую область, как у меня, тогда вам все равно придется перебирать числа. Не самое продуманное решение за всю историю. Вероятно, так же хорошо, что вам не нужно растягивать.