I need to have a straight line( horizontal/vertical) in my Diagram.But the NodeFigure.Line1 or Line 2 gives me a straight line but its is a diagonal line.
How can I have a straight line in my diagram? Also this line should be resizable as required.I’ve added a HorizontalLine template. This is implemented in a manner very similar to how a Path with a NodePanel.Figure is implemented by a containing NodePanel.
Note that it has a custom ResizeAdornmentTemplate that creates only the two resize handles at the middle sides.
However, there appears to be a bug in being able to select it. We’ll investigate.
[code]
<UserControl.Resources>
<DataTemplate x:Key="Astronaut">
<Border BorderBrush="Silver" BorderThickness="3" CornerRadius="5" Padding="5" Background="White">
<StackPanel>
<TextBlock Text="{Binding Path=Data.Text}" FontWeight="Bold" FontSize="12"/>
<TextBlock Text="Astronaut" FontStyle="Italic" FontSize="10" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Mass: " />
<TextBlock Text="{Binding Path=Data.Mass}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Weight: " />
<TextBlock Text="{Binding Path=Data.CurrentWeight}" />
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate x:Key="RaceCarDriver">
<Border BorderBrush="Red" BorderThickness="3" Padding="5" Background="White">
<StackPanel>
<Border Background="DarkBlue">
<TextBlock Text="{Binding Path=Data.Text}" FontWeight="Bold" FontSize="12" Foreground="White" />
</Border>
<TextBlock Text="Race Car Driver" FontStyle="Italic" FontSize="10" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Weight: " />
<TextBlock Text="{Binding Path=Data.Mass}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Speed: " />
<TextBlock Text="{Binding Path=Data.CurrentSpeed}" />
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
<font color="#ff0000">[Edit: this has been replaced by a better definition, in a reply below.]</font>
<DataTemplate x:Key="HorizontalLine">
<Path Stroke="Black" StrokeThickness="5" Stretch="Fill"
go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}"
Width="{Binding Path=Data.Length, Mode=TwoWay}" Height="5"
go:Part.SelectionAdorned="False" go:Part.Resizable="True">
<Path.Data>
<LineGeometry StartPoint="0 0" EndPoint="1 0" />
</Path.Data>
<go:Part.ResizeAdornmentTemplate>
<DataTemplate>
<go:SpotPanel>
<Path go:NodePanel.Figure="Rectangle" go:SpotPanel.Spot="0.0 0.5" Width="6" Height="6" Fill="DodgerBlue" Stroke="Black" StrokeThickness="1" />
<Path go:NodePanel.Figure="Rectangle" go:SpotPanel.Spot="1.0 0.5" Width="6" Height="6" Fill="DodgerBlue" Stroke="Black" StrokeThickness="1" />
</go:SpotPanel>
</DataTemplate>
</go:Part.ResizeAdornmentTemplate>
</Path>
</DataTemplate>
</UserControl.Resources>
[/code]I’ve added a base “Item” class, with “HorizontalLine” inheriting from it.
Under the assumption that each data class will have its own DataTemplate, it’s a pain to have to maintain a DataTemplateDictionary. So I have customized the PartManager to automatically return the DataTemplate that is named the same as the class name. No DataTemplateDictionary needed!
[code]using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using Northwoods.GoXam;
using Northwoods.GoXam.Model;
namespace SilverlightApplication1 {
public partial class MultiDataModel : UserControl {
public MultiDataModel() {
InitializeComponent();
// instead of using and maintaining a DataTemplateDictionary,
// implement its usage in an override of PartManager.FindTemplateForNode
myDiagram.PartManager = new CustomPartManager();
// create a trivial model holding one instance of each node data class
var model1 = new GraphModel<Item, String>();
model1.NodesSource = new List<Item>() {
new Person() { Key="1", Text="Joe Random", Mass=50 },
new Astronaut() { Key="2", Text="Joe Wright", Mass=50, CurrentWeight=0.01 },
new RaceCarDriver() { Key="3", Text="Fast Joe", Mass=50, CurrentSpeed=203 },
new HorizontalLine() { Key="4" },
};
this.Model1 = model1;
// start off showing this model
myDiagram.Model = this.Model1;
}
internal GraphModel<Item, String> Model1 { get; set; }
private void Button1_Click(object sender, RoutedEventArgs e) {
myDiagram.Model = this.Model1;
}
private void Button2_Click(object sender, RoutedEventArgs e) {
var model2 = new GraphModel<Item, String>();
model2.NodesSource = GeneratePeople();
myDiagram.Model = model2;
}
private IList<Item> GeneratePeople() {
var people = new List<Item>();
Random rand = new Random();
int num = rand.Next(10) + 3;
for (int i = 0; i < num; i++) {
Person p;
switch (rand.Next(3)) {
case 1: p = new Astronaut() { Category="Astronaut" }; break;
case 2: p = new RaceCarDriver() { Category="RaceCarDriver" }; break;
default: p = new Person() { Category="" }; break;
}
p.Key = i.ToString();
p.Text = "Person #" + i.ToString();
p.Mass = rand.Next(100);
people.Add(p);
}
return people;
}
}
public abstract class Item : GraphModelNodeData { }
public class Person : Item {
public double Mass {
get { return _Mass; }
set {
if (_Mass != value) {
double old = _Mass;
_Mass = value;
RaisePropertyChanged(“Mass”, old, value);
}
}
}
private double _Mass;
}
public class Astronaut : Person {
public double CurrentWeight {
get { return _CurrentWeight; }
set {
if (_CurrentWeight != value) {
double old = _CurrentWeight;
_CurrentWeight = value;
RaisePropertyChanged(“CurrentWeight”, old, value);
}
}
}
private double _CurrentWeight;
}
public class RaceCarDriver : Person {
public double CurrentSpeed {
get { return _CurrentSpeed; }
set {
if (_CurrentSpeed != value) {
double old = _CurrentSpeed;
_CurrentSpeed = value;
RaisePropertyChanged(“CurrentSpeed”, old, value);
}
}
}
private double _CurrentSpeed;
}
public class HorizontalLine : Item {
public double Length {
get { return _Length; }
set {
if (_Length != value) {
double old = _Length;
_Length = value;
RaisePropertyChanged(“Length”, old, value);
}
}
}
private double _Length = 100;
}
public class CustomPartManager : PartManager {
// If we assume each data class has a different DataTemplate,
// this is easier than having to maintain a DataTemplateDictionary.
// However, this way can’t arbitrarily use different templates for each instance of any class.
protected override DataTemplate FindTemplateForNode(object nodedata, IDiagramModel model, bool isgroup, bool islinklabel, string category) {
String classname = nodedata.GetType().Name;
DataTemplate template = Diagram.FindResource(this.Diagram, classname);
return template ?? base.FindTemplateForNode(nodedata, model, isgroup, islinklabel, category);
}
}
}[/code]
Yes, I observed some problem when I select the Horizontal line after selecting it from the panel.
Not yet – it appears to be a bug/misfeature in Silverlight’s VisualTreeHelper. We’re working on it.
One of us has discovered that wrapping the Path in a NodePanel or Border seems to fix all of the selection problems with a HorizontalLine in Silverlight.
Here’s the updated template:
<DataTemplate x:Key="HorizontalLine">
<Border go:Part.SelectionElementName="Shape"
go:Part.SelectionAdorned="True"
go:Part.Resizable="True"
go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
<Path x:Name="Shape" Stroke="Black" StrokeThickness="3" Stretch="Fill"
Width="{Binding Path=Data.Length, Mode=TwoWay}">
<Path.Data>
<LineGeometry StartPoint="0 0" EndPoint="1 0" />
</Path.Data>
</Path>
<go:Part.ResizeAdornmentTemplate>
<DataTemplate>
<go:SpotPanel>
<!-- use go:ToolHandle in WPF, Path in Silverlight -->
<Path go:NodePanel.Figure="Rectangle" go:SpotPanel.Spot="0.0 0.5" Width="6" Height="6" Fill="DodgerBlue" Stroke="Black" StrokeThickness="1" />
<Path go:NodePanel.Figure="Rectangle" go:SpotPanel.Spot="1.0 0.5" Width="6" Height="6" Fill="DodgerBlue" Stroke="Black" StrokeThickness="1" />
</go:SpotPanel>
</DataTemplate>
</go:Part.ResizeAdornmentTemplate>
</Border>
</DataTemplate>
This template is for Silverlight. For WPF, replace the two Paths in the ResizeAdornmentTemplate with go:ToolHandle.