tags:

views:

266

answers:

1

Have (finally) created a baseball diamond control using xaml. (code below). I now need the ability to create "clickable" text at the major positions (1B, 2B, SS, 3B, etc). The text also needs to be rotated (since I draw this entire control in the corner and then rotate it at the end.

Can someone assist in adding text to my DrawingGroup? (bouns if it's clickable).

Any other comments appreciated, I'm brand new to Wpf, so I don't even know if I'm doing this correctly. My first attempt drew the diamond in code, but I wanted to challenge myself to completely defining it in XAML.

<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="528.303" Width="582.133">
<Grid Background="#C0E49C">
    <Image HorizontalAlignment="Stretch" VerticalAlignment="bottom"> 
        <Image.Source>
            <DrawingImage>
                <DrawingImage.Drawing>
                    <DrawingGroup>
                        <DrawingGroup.Transform>
                                <TransformGroup>
                                    <RotateTransform CenterX="0" CenterY="0" Angle="-135" />
                                <TranslateTransform X="0" Y="-4" />
                            </TransformGroup>
                        </DrawingGroup.Transform>
                    <GeometryDrawing Brush="#FFC080" >
                        <GeometryDrawing.Pen>
                            <Pen Brush="Black" Thickness="1"/>
                        </GeometryDrawing.Pen> 
                    <GeometryDrawing.Geometry>
                        <GeometryGroup>
                           <PathGeometry>
                            <PathGeometry.Figures>
                                <PathFigureCollection>
                                    <PathFigure StartPoint="0,0">
                                        <PathFigure.Segments>
                                            <PathSegmentCollection>
                                                <LineSegment Point="500,0" />
                                                <BezierSegment Point1="606,350"
                                                       Point2="350,606"
                                                       Point3="0,500"
                                                       />
                                                <LineSegment Point="0,0" />
                                            </PathSegmentCollection>
                                        </PathFigure.Segments>
                                    </PathFigure>
                                </PathFigureCollection>                                   
                            </PathGeometry.Figures>
                         </PathGeometry>
                        <RectangleGeometry Rect="8,8,333,333" />
                        <EllipseGeometry Center="174.5,174.5" RadiusX="50" RadiusY="50" />

                      </GeometryGroup>
            </GeometryDrawing.Geometry>
        </GeometryDrawing>

    </DrawingGroup>

+1  A: 

The only way to add text to a DrawingGroup would be through GlyphRunDrawing. This is a very low-level class. I would not recommend it to the average user.

There is a better way to go about this: you have your baseball diamond set up as a background image, why not simply place text on top of the image?

Change your root-level Grid to a Viewbox. Place your Grid inside the Viewbox.

Second, add a new class file to your project, called "SelectableTextblock." Here is the code-behind for that class:

public class SelectableTextBlock : TextBlock
{
 public bool IsSelected
 {
  get { return (bool)this.GetValue(IsSelectedProperty); }
  set { this.SetValue(IsSelectedProperty, value); }
 }

 public static readonly DependencyProperty IsSelectedProperty =
  DependencyProperty.Register("IsSelected", typeof(bool), typeof(SelectableTextBlock), new PropertyMetadata(false, IsSelectedPropertyChanged));

 static void IsSelectedPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
 {
  SelectableTextBlock textBlock = obj as SelectableTextBlock;
  textBlock.Background = (bool)e.NewValue ? new SolidColorBrush(Color.FromArgb(200, 255, 255, 255)) : Brushes.Transparent;
 }

 protected override void OnMouseDown(MouseButtonEventArgs e)
 {
  IsSelected = !IsSelected;
  base.OnMouseDown(e);
 }
}

Quite simply, this just declares a DependencyProperty that can be either selected or unselected. It acts as a toggle: when you click it, you select the text; click it again and it becomes unselected.

Now, declare the local namespace in your XAML, and then add a SelectableTextBlock for each position in your diamond:

<local:SelectableTextBlock>
  1st Base
</local:SelectableTextBlock>

Here is the end result:

<Window x:Class="TestWpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
Title="Window1"
Background="#C0E49C">
<Viewbox>
 <Grid>
  <Image HorizontalAlignment="Stretch" VerticalAlignment="bottom">
   <Image.Source>
    <DrawingImage>
     <DrawingImage.Drawing>
      <DrawingGroup>
       <DrawingGroup.Transform>
        <TransformGroup>
         <RotateTransform CenterX="0" CenterY="0" Angle="-135" />
         <TranslateTransform X="0" Y="-4" />
        </TransformGroup>
       </DrawingGroup.Transform>
       <GeometryDrawing Brush="#FFC080" >
        <GeometryDrawing.Pen>
         <Pen Brush="Black" Thickness="1"/>
        </GeometryDrawing.Pen>
        <GeometryDrawing.Geometry>
         <GeometryGroup>
         <PathGeometry>
          <PathGeometry.Figures>
           <PathFigureCollection>
            <PathFigure StartPoint="0,0">
             <PathFigure.Segments>
              <PathSegmentCollection>
               <LineSegment Point="500,0" />
               <BezierSegment Point1="606,350"
                              Point2="350,606"
                              Point3="0,500" />
               <LineSegment Point="0,0" />
              </PathSegmentCollection>
             </PathFigure.Segments>
            </PathFigure>
           </PathFigureCollection>
          </PathGeometry.Figures>
         </PathGeometry>
         <RectangleGeometry Rect="8,8,333,333" />
         <EllipseGeometry Center="174.5,174.5" RadiusX="50" RadiusY="50" />
        </GeometryGroup>
       </GeometryDrawing.Geometry>
      </GeometryDrawing>
     </DrawingGroup>
    </DrawingImage.Drawing>
   </DrawingImage>
  </Image.Source>
 </Image>
 <local:SelectableTextBlock Margin="480, 60, 0, 0"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center">
  1st Base
 </local:SelectableTextBlock>
</Grid>

Charlie
Nicely done, sir.Would it have been possible for him to use the adorner layer on something like this? Simply adorn certain elements of his xaml baseball diamond? I'm really asking... no idea.
Anderson Imes
Thanks, Anderson! As for adorners, unfortunately they could not be used in this case. Adorners can only be applied to UIElements, and since the System.Windows.Drawing is not a UIElement (just a DependencyObject), I don't believe an adorner can be applied to it. Good question, though.
Charlie
thanks very much for taking the time to do this. I really appreciate it. One problem though - in my original code, the baseball diamond would automatically resize to fit in the window. After making your changes, the diamond no longer resizes. It also appears that your selectabletextblock will not move along with the resizing diamond, because it's at fixed coordinates. Thanks again VERY MUCH.
taglius
To solve that problem, it's going to take some tricks with Viewbox and Grid. I'll edit my post accordingly.
Charlie
Updated the XAML and the result now resizes properly, and holds position.
Charlie
this works beautifully. thanks so much!
taglius
You should accept my answer so I get points!
Charlie
done. one other question - using the "margin" property to place the textbox is freaking me out a bit. How does "margin" work? Is it from the center? What type of units are they in?
taglius
Margin works almost exactly the same as Canvas' Top, Left, Right, Bottom. It is in device independent pixels. It is from the position of the element, so in this case since I've set the HorizontalAlignment and VerticalAlignment to Center, it is from the center. So basically it depends on the alignment properties.
Charlie