tags:

views:

59

answers:

3

I have a chart with some lines in a WPF application.

When the line is (mouse) selected by the user I need to change its look. à la :

alt text

In WPF the two lines code could be as follows:

        <Line
            X1= "10" Y1="20" X2="150" Y2="20"
            Stroke="Black" StrokeThickness="1" />

        <Line
            X1= "10" Y1="50" X2="150" Y2="50"
            Stroke="Blue" StrokeThickness="3" />  
        <Polygon 
            Points="80,45 80,55 90,50" 
            Stroke="Blue" Fill="Blue" />
  1. How to change the line 'style' when the user selects the object (Line)?

  2. Is it possible to keep the "arrow" in the middle of the segment?

A: 

Is there a reason why you can't do this in code behind? So add a MouseDown event to the line, and then do something like this in the MouseDown event:

private void Line_MouseDown( object sender, MouseButtonEventArgs e )
{
    ( (Line)sender ).Stroke = Brushes.Blue;
}

Plus change the stroke thickness and add the arrow to the middle of the line. Similarly you could declare a local variable to hold the line that is selected so when another line is selected you can reset the style on the previously selected one. Would that not work?

Matthew Davies
So, you suggests to each MouseDown chanage by the code the line stroke and add to the canvas a new polygon (mathematically calculating its coordinates in dependency of line coordinates). And so for each Selected line in the graph... Maybe... And when unselected, find and remove corresponding polygon....
serhio
+2  A: 

I would simply create a custom control inheriting from Shape (you cannot inherit from Line because it is sealed).

The new control would have an IsSelected property that gets set to true OnMouseDown and false OnMouseUp. It would also have properties that determine the placement and rotation of the Arrow.

Then, in the ControlTemplate of the generic style, you would simply have a DataTrigger that specifies the Stroke and StrokeThickness when the property IsSelected is set to True. Of course, these could also be properties of the new class (SelectedStroke and SelectedStrokeThickness) that could be set using TemplateBinding.

Also, the arrow would always be part of the ControlTemplate, with Visibility set to Collapsed. In the IsSelected DataTrigger, the Visibility would then be set to Visible.

I think that you'll find doing this makes it much more extensible, easier to maintain, etc.

Wonko the Sane
+2  A: 

You could define your Arrow shape like the following (replace all shown properties with dependency properties. One example shown.). You could then add a IsSelected property and set the selection style with a DataTrigger that watches that property. You could even set up an animation for the selected arrow that way.

   public class Arrow : Shape
    {

        public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof(double), typeof(Arrow), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

        [TypeConverter(typeof(LengthConverter))]
        public double X1
        {
        get { return (double)base.GetValue(X1Property); }
        set { base.SetValue(X1Property, value); }
        }

        // TODO: Replace with dependency properties
        [TypeConverter(typeof(LengthConverter))]
        public double X2 { get; set; }

        [TypeConverter(typeof(LengthConverter))]
        public double Y1 { get; set; }

        [TypeConverter(typeof(LengthConverter))]
        public double Y2 { get; set; }

        [TypeConverter(typeof(LengthConverter))]
        public double ArrowWidth { get; set; }

        [TypeConverter(typeof(LengthConverter))]
        public double ArrowLength { get; set; }



        protected override Geometry DefiningGeometry
        {
            get 
            { 
                var geometryGroup = new GeometryGroup();

                // Add arrow head
                var midPoint = new Point((X1 + X2) / 2.0, (Y1 + Y2) / 2.0);
                double dX = (X2 - X1);
                double dY = (Y2 - Y1);
                double length = Math.Sqrt(dX*dX + dY*dY);
                dX /= length;
                dY /= length;
                var myPathSegmentCollection = new PathSegmentCollection 
                    {
                        new LineSegment {Point = new Point(midPoint.X - dX * ArrowLength + dY * ArrowWidth/2.0, midPoint.Y - dY * ArrowLength - dX * ArrowWidth/2.0)},
                        new LineSegment {Point = new Point(midPoint.X - dX * ArrowLength - dY * ArrowWidth/2.0, midPoint.Y - dY * ArrowLength + dX * ArrowWidth/2.0)},
                        new LineSegment {Point = midPoint},
                    };
                var myPathFigure = new PathFigure {StartPoint = midPoint, Segments = myPathSegmentCollection};

                var myPathFigureCollection = new PathFigureCollection {myPathFigure};

                var myPathGeometry = new PathGeometry {Figures = myPathFigureCollection};
                geometryGroup.Children.Add(myPathGeometry);
                // Add line
                geometryGroup.Children.Add(new LineGeometry(new Point(X1, Y1), new Point(X2, Y2)));
                return geometryGroup;
            }
        }
    }

Update Added a single example dependency property.

Holstebroe
this wotked whell, thans. What is the meaning of the [TypeConverter(typeof(LengthConverter))]?
serhio
It is a specification of the default type converter from other types to and from the X1, Y1 coordinates. XAML uses the default type converter to convert to / from a string representation of the coordinate. LengthConverter is a bit mis-guiding name, but this is what the WPF framework uses for it's Line coordinates.
Holstebroe
by the way, this X1... dependency properties does not behave in designer like the standard Line ones. I mean, When I change them in XAML, the custom line is not updated automatically in the designer.
serhio
this is, by the way, an other question: http://stackoverflow.com/questions/3770310/dependencyproperty-and-wpf-designer
serhio
That is strange the FrameworkPropertyMetadataOptions.AffectsRender is set in the X1 property. You might need to recompile before the dependency property works in the designer.
Holstebroe