I'm using the following data template:
<DataTemplate>
<Grid Width="40" Height="40">
<Ellipse Width="30" Height="30" x:Name="ellipse" />
<TextBlock Text="{Binding Robot.Id}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Line X1="20" X2="40" X2="20" X2="30" x:Name="line" />
</Grid>
</DataTemplate>
I want to apply the following code to the line when a property of the DataContext changes:
void UpdateHeading(double angle)
{
var center = grid.Width/2;
var radius = ellipse.Width/2;
line.X1 = center + (radius+5)*Math.Sin(angle);
line.Y1 = center + (radius+5)*Math.Cos(angle);
line.X2 = center + (radius-5)*Math.Sin(angle);
line.Y2 = center + (radius-5)*Math.Cos开发者_JS百科(angle);
}
Note that the code needs access to the size of two other elements
What is the best way to add the code? using a value converter doesn't seem right here, since I need to convert one property to four
There is also an IMultiValueConverter
EDIT: You should have a ViewModel with a angle property and can then bind as the following (i have only demonstrated for x1:
<DataTemplate>
<Grid Width="40" Height="40" x:Name="grid">
<Ellipse Width="30" Height="30" x:Name="ellipse" />
<TextBlock Text="{Binding Robot.Id}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Line X2="40" X2="20" X2="30" x:Name="line">
<Line.X1>
<MultiBinding Converter="{StaticResource yourConverter}" ConverterParameter="{yourns:Enum">
<Binding ElementName=grid Path=Width />
<Binding ElementName=ellipse Path=Width />
<Binding Path=Angle />
</MultiBinding>
</Line.X1>
</Line>
</Grid>
</DataTemplate>
Implement INotifyPropertyChanged in you class that is providing the DataContext and expose properties for all the values that need to be updated. Then just bind to those properties. The properties can calculate whatever values they need and the UI will get updated.
For Example:
public class Heading : INotifyPropertyChanged
{
private string name = "";
public string Name { get { return name; } set { name = value; SendPropertyChanged("Name"); } }
public int Radius { get { return GridWidth/2; } }
public double X1 { get { return Center + (Radius + 5) * Math.Sin(Angle); } }
public double X2 { get { return Center + (Radius + 5) * Math.Cos(Angle); } }
public double Y1 { get { return Center + (Radius - 5) * Math.Sin(Angle); } }
public double Y2 { get { return Center + (Radius - 5) * Math.Cos(Angle); } }
public int Center { get { return GridWidth/2; } }
private int gridWidth = 50;
public int GridWidth { get { return gridWidth; } set { gridWidth = value; } }
private double angle;
public double Angle { get { return angle; } set { angle = value; SendPropertyChanged(""); } } //Empty string to notify of all properties
public event PropertyChangedEventHandler PropertyChanged;
void SendPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then just set the binding in you template as so:
<DataTemplate>
<Grid >
<Ellipse Width="{Binding GridWidth}" Height="40" x:Name="ellipse" Fill="Green" />
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Line X1="{Binding X1}" X2="{Binding X2}" Y1="{Binding Y1}" Y2="{Binding Y2}" x:Name="line" Stroke="Black" />
</Grid>
</DataTemplate>
Hope this helps.
I decided to create my own shape.
XAML usage:
<Grid Width="40" Height="40">
<Ellipse x:Name="ellipse" Width="30" Height="30" />
<TextBlock Text="{Binding Robot.Id}" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Controls:HeadingLine BoundingSize="40" ShapeSize="30" Length="10" Angle="{Binding Heading}" Stroke="Black" StrokeThickness="1" />
</Grid>
And code:
public sealed class HeadingLine : Shape
{
// Properties definitions
....
// Code based on http://www.codeproject.com/KB/WPF/wpfarrow.aspx
protected override Geometry DefiningGeometry
{
get
{
var geometry = new StreamGeometry();
using (var context = geometry.Open())
{
InternalDrawArrowGeometry(context);
}
geometry.Freeze();
return geometry;
}
}
private void InternalDrawArrowGeometry(StreamGeometryContext context)
{
var center = BoundingSize / 2;
var radius = ShapeSize / 2;
var offset = Length / 2;
var angle = Math.PI - Angle;
var x1 = center + (radius + offset) * Math.Sin(angle);
var y1 = center + (radius + offset) * Math.Cos(angle);
var x2 = center + (radius - offset) * Math.Sin(angle);
var y2 = center + (radius - offset) * Math.Cos(angle);
context.BeginFigure(new Point(x1, y1), false, false);
context.LineTo(new Point(x2, y2), true, true);
}
}
精彩评论