Reputation: 471
Im trying to represent weighted graph with MVVM inside canvas so im representing graphs vertices and edges as observable collections and putting them into canvas ItemsControl. But I cant find any reasonable way to position text that represents weight on the center of line(graphs edge)
my 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>
Upvotes: 0
Views: 1355
Reputation: 37066
Add to your EdgeItem
class (or whatever's in your EdgeItems
collection).
// 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);
I'm assuming EdgeItem
has a Weight
property -- if not, Weight
is a stand-in for whatever property you want to display in the center of the line. I would have thought Canvas.Left
and Canvas.Top
would work, but for me they don't in this case.
<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>
Alternatively, don't add the Margin
, Left
, and Top
properties to EdgeItem
, and generate the margin Thickness
with a value converter instead. I'm not crazy about having those properties on EdgeItem
, but then again on the other hand, a value converter on {Binding}
is a problem with raising PropertyChanged
, if you happen to alter V1
or V2
at runtime. The solution to that would be to make it a multi value converter and bind V1
and V2
separately with a multi binding. I just got lazy about writing that 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>
Converter:
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();
}
}
Upvotes: 1