#c# #wpf #xaml #canvas #binding
#c# #wpf #xaml #холст #привязка
Вопрос:
Я пытаюсь представить взвешенный граф с MVVM внутри canvas, поэтому я представляю вершины и ребра графов как наблюдаемые коллекции и помещаю их в canvas ItemsControl. Но я не могу найти какой-либо разумный способ расположить текст, представляющий вес, по центру строки (край графика)
мой canvas xaml:
<Canvas Background="Linen" ClipToBounds="True"
Grid.Row="0" Grid.Column="0">
<ItemsControl ItemsSource="{Binding EdgeItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Line Stroke="Black" StrokeThickness="4"
X1="{Binding V1.X}" Y1="{Binding V1.Y}"
X2="{Binding V2.X}" Y2="{Binding V2.Y}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
Ответ №1:
Добавьте в свой EdgeItem
класс (или что-то еще в вашей EdgeItems
коллекции).
// When V1 or V2 changes, raise PropertyChanged("Margin")
public Thickness Margin => new Thickness(Left, Top, 0, 0);
public double Left => Math.Min(V1.X, V2.X);
public double Top => Math.Min(V1.Y, V2.Y);
Я предполагаю, что EdgeItem
имеет Weight
свойство — если нет, Weight
является заменой для любого свойства, которое вы хотите отобразить в центре строки. Я бы подумал, что Canvas.Left
и Canvas.Top
это сработает, но для меня в данном случае это не так.
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Line
Stroke="Black"
StrokeThickness="4"
X1="{Binding V1.X}"
Y1="{Binding V1.Y}"
X2="{Binding V2.X}"
Y2="{Binding V2.Y}"
/>
<Label
Background="#ccffffff"
Content="{Binding Weight}"
Margin="{Binding Margin}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
Второй способ
В качестве альтернативы, не добавляйте Margin
свойства Left
Top
, EdgeItem
и в Thickness
, а вместо этого сгенерируйте поле, используя преобразователь значений. Я не в восторге от включения этих свойств EdgeItem
, но опять же, с другой стороны, при включенном преобразователе значений {Binding}
возникает проблема с повышением PropertyChanged
, если вы случайно измените V1
или V2
во время выполнения. Решением этого было бы сделать его преобразователем нескольких значений и привязать V1
и V2
отдельно с помощью множественной привязки. Я просто поленился писать этот XAML.
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Line
Stroke="Black"
StrokeThickness="4"
X1="{Binding V1.X}"
Y1="{Binding V1.Y}"
X2="{Binding V2.X}"
Y2="{Binding V2.Y}"
/>
<Label
Background="#ccffffff"
Content="{Binding Weight}"
Margin="{Binding Converter={local:EdgeItemMargin}}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
Конвертер:
public class EdgeItemMargin : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
var edge = (EdgeItem)value;
return new Thickness(
Math.Min(edge.V1.X, edge.V2.X),
Math.Min(edge.V1.Y, edge.V2.Y),
0, 0);
}
public object ConvertBack(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
Комментарии:
1. Просто любопытно: почему вы выводите EdgeItemMargin из формы MarkupExtension?
2. Привычка @HenrikHansen. Таким образом, им можно присвоить именованные и типизированные (но не привязанные) параметры, хотя в данном случае в этом нет необходимости.