views:

418

answers:

1

I'm creating a game desk. I wanted to specify field size (one field is a square) as a attached property and with this data set value of ViewPort which would draw 2x2 matrix (and tile mode would do the rest of game desk).

I'm quite at loss what is wrong because the binding doesn't work.

Testing line in XAML for the behaviour I would like to have:

<DrawingBrush Viewport="0,0,100,100" ViewportUnits="Absolute" TileMode="None">

The game desk is based on this sample of DrawingPaint: http://msdn.microsoft.com/en-us/library/aa970904.aspx (an image is here)

XAML:

<Window x:Class="Sokoban.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Sokoban"
    Title="Window1" Height="559" Width="419">
    <Window.Resources>
        <local:FieldSizeToRectConverter x:Key="fieldSizeConverter" />
        <Style x:Key="GameDesk" TargetType="{x:Type Rectangle}">
            <Setter Property="local:GameDeskProperties.FieldSize" Value="50" />
            <Setter Property="Fill">
                <Setter.Value>
                    <!--<DrawingBrush Viewport="0,0,100,100" ViewportUnits="Absolute" TileMode="None">-->
                    <DrawingBrush Viewport="{TemplateBinding local:GameDeskProperties.FieldSize, Converter={StaticResource fieldSizeConverter}}" ViewportUnits="Absolute" TileMode="None">
                        <DrawingBrush.Drawing>
                            <DrawingGroup>
                                <GeometryDrawing Brush="CornflowerBlue">
                                    <GeometryDrawing.Geometry>
                                        <RectangleGeometry Rect="0,0,100,100" />
                                    </GeometryDrawing.Geometry>
                                </GeometryDrawing>

                                <GeometryDrawing Brush="Azure">
                                    <GeometryDrawing.Geometry>
                                        <GeometryGroup>
                                            <RectangleGeometry Rect="0,0,50,50" />
                                            <RectangleGeometry Rect="50,50,50,50" />
                                        </GeometryGroup>
                                    </GeometryDrawing.Geometry>
                                </GeometryDrawing>
                            </DrawingGroup>
                        </DrawingBrush.Drawing>
                    </DrawingBrush>
               </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <StackPanel>
        <Rectangle Style="{StaticResource GameDesk}" Width="300" Height="150" />        
    </StackPanel>
</Window>

Converter and property definition:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Diagnostics;
using System.Windows.Data;

namespace Sokoban
{
    public class GameDeskProperties : Panel
    {

        public static readonly DependencyProperty FieldSizeProperty;

        static GameDeskProperties()
        {
            PropertyChangedCallback fieldSizeChanged =
                new PropertyChangedCallback(OnFieldSizeChanged);
            PropertyMetadata fieldSizeMetadata =
                new PropertyMetadata(50, fieldSizeChanged);

            FieldSizeProperty = DependencyProperty.RegisterAttached("FieldSize",
                typeof(int), typeof(GameDeskProperties), fieldSizeMetadata);
        }

        public static int GetFieldSize(DependencyObject target)
        {
            return (int)target.GetValue(FieldSizeProperty);
        }

        public static void SetFieldSize(DependencyObject target, int value)
        {
            target.SetValue(FieldSizeProperty, value);
        }


        static void OnFieldSizeChanged(DependencyObject target,
                              DependencyPropertyChangedEventArgs e)
        {
            Debug.WriteLine("FieldSize just changed: " + e.NewValue);
        }
    }

    [ValueConversion(/* sourceType */ typeof(int), /* targetType */ typeof(Rect))]
    public class FieldSizeToRectConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Debug.Assert(targetType == typeof(int));

            int fieldSize = int.Parse(value.ToString());
            return new Rect(0, 0, 2 * fieldSize, 2 * fieldSize);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // should not be called in our example
            throw new NotImplementedException();
        }
    }
}
+1  A: 

TemplateBindings only work for dependency properties defined on the control being templated (in a ControlTemplate). You just need to switch this around to be a Binding with a RelativeSource of AncestorType (also, attached properties require parenthesis to be used in a binding):

...
<DrawingBrush Viewport="{Binding Path=(local:GameDeskProperties.FieldSize), Converter={StaticResource fieldSizeConverter}, RelativeSource={RelativeSource AncestorType={x:Type Rectangle}}}"
...

Edit Updated the RelativeSource binding, as it was not defined inside of a ControlTemplate.

Abe Heidebrecht
Thank you. It doesn't work still. There must be more problems. I tried to put a breakpoint in the Convert method and it's not called. How is it possible?
MartyIX
I've found this error in output window of Visual Studio:System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=(0); DataItem=null; target element is 'DrawingBrush' (HashCode=35191196); target property is 'Viewport' (type 'Rect')
MartyIX
Sorry, I didn't look closely at the property you were setting. I've updated the answer so that it actually works.
Abe Heidebrecht
Thanks, I've found out from your previous example.
MartyIX