0

I've read some books (galli) and did some research here on the site. The problem is I think I didn't understand it.

What I tried to do is to create a button of a custom shape (hexagon). When the user clicks it, it should change the Color.

After finding this and that, I created the following:

 public class hexButton : System.Windows.Controls.Button
{
    public SolidColorBrush borderColor { get; set; }
    public SolidColorBrush fillColor {get;set;}
    public int Height { get; private set; }
    public int Width { get; private set; }
    public Point Location { get; private set; }
    private Hexagon hex;

    public hexButton() : this(50,0,0,Colors.Yellow, Colors.Brown){}    

    public hexButton(int sideLenght, int positionX, int positionY, Color border, Color fill):base()
    {
        borderColor = new SolidColorBrush(border);
        fillColor = new SolidColorBrush(fill);
        hex = new Hexagon(sideLenght, positionX, positionY);

        Binding binding = new Binding();
        binding.Path = new PropertyPath("FillProperty");
        binding.Source = fillColor;
        BindingOperations.SetBinding(hex.polygon, Polygon.FillProperty, binding);

        ControlTemplate t = new ControlTemplate(typeof(Button));
        FrameworkElementFactory fef = new FrameworkElementFactory(typeof(Polygon));
        fef.Name = "Polygon";
        fef.SetValue(Polygon.PointsProperty, hex.polygon.Points);
        fef.SetBinding(Polygon.FillProperty, binding); //doesent work
        fef.SetValue(Polygon.StrokeThicknessProperty, hex.polygon.StrokeThickness);
        //fef.SetValue(Polygon.FillProperty, fillColor); // works at least for the constructor
        fef.SetValue(Polygon.StrokeProperty, borderColor);
        t.VisualTree = fef;

        Style hexButtonStyle = new Style(typeof(Button));
        hexButtonStyle.Setters.Add(new Setter(Button.TemplateProperty, t));

        this.Style = hexButtonStyle;
        this.Height = hex.Y_Max - hex.Y_Min;
        this.Width = hex.X_Max - hex.X_Min;
        this.Location = hex.location;
    }

I also have a hexagon class:

    public class Hexagon
{
    private int _Height;
    private int _Diameter;
    private int _SideLenght;
    public Polygon polygon { get; set; }
    public int Y_Min { get; private set; }
    public int Y_Max { get; private set; }
    public int X_Min { get; private set; }
    public int X_Max { get; private set; }
    public Point location { get; set; }

    public Hexagon() : this(30) { }

    public Hexagon(int sideLenght): this (sideLenght,0,0) { }

    public Hexagon(int sideLenght, Point location): this(sideLenght,(int)location.X, (int)location.Y){}

    public Hexagon(int sideLenght, int locationX, int locationY)
    {
        if (sideLenght <= 0)
        {
            sideLenght = 30;
        }
        _SideLenght = sideLenght;
        calcHeight();
        calcDiameter();
        location = new Point(locationX, locationY);
        polygon = new Polygon();
        polygon.Points.Clear();
        polygon.StrokeThickness = 4;
        createHexPolygon();
    }

    private void calcHeight()
    {
        //h = (√3)s
        _Height = Convert.ToInt32(Math.Pow(3,0.5)*_SideLenght);
    }

    private void calcDiameter()
    {
        _Diameter = 2 * _SideLenght;
    }

    private void createHexPolygon()
    {   //        0           1
        //         __________
        //        /          \
        //       /            \
        //      /              \  2
        //    5 \              /
        //       \            /
        //        \__________/
        //        4           3

        Point p = new Point();
        p.X = _SideLenght / 2;
        p.Y = 0;
        p.X = p.X + location.X + 2; // +2 =offset von StrokeThickness (siehe konstruktor)
        p.Y = p.Y + location.Y + 2;
        polygon.Points.Add(p);
        p.X = _SideLenght + (_SideLenght / 2);
        p.Y = 0;
        p.X = p.X + location.X + 2;
        p.Y = p.Y + location.Y + 2;
        polygon.Points.Add(p);
        p.X = _Diameter;
        p.Y = _Height / 2;
        p.X = p.X + location.X + 2;
        p.Y = p.Y + location.Y + 2;
        polygon.Points.Add(p);
        p.X = _SideLenght + (_SideLenght / 2);
        p.Y = _Height;
        p.X = p.X + location.X + 2;
        p.Y = p.Y + location.Y + 2;
        polygon.Points.Add(p);
        p.X = _SideLenght / 2;
        p.Y = _Height;
        p.X = p.X + location.X + 2;
        p.Y = p.Y + location.Y + 2;
        polygon.Points.Add(p);
        p.X = 0;
        p.Y = _Height / 2;
        p.X = p.X + location.X + 2;
        p.Y = p.Y + location.Y + 2;
        polygon.Points.Add(p);

        X_Min = (int)polygon.Points[5].X;
        X_Max = (int)polygon.Points[2].X + 2;
        Y_Min = (int)polygon.Points[0].Y;
        Y_Max = (int)polygon.Points[3].Y + 2;
    }

}

For later use (when clicking the button - property changed event necessary) I wanted to use this to reset the binding:

        private void SetBinding()
    {
        Binding binding = new Binding();
        binding.Path = new PropertyPath("FillProperty");
        binding.Source = fillColor;
        BindingOperations.SetBinding(hex.polygon, Polygon.FillProperty, binding);
    }

I think the problem is somewhere in here:

Binding binding = new Binding();
        binding.Path = new PropertyPath("FillProperty");
        binding.Source = fillColor;
        BindingOperations.SetBinding(hex.polygon, Polygon.FillProperty, binding);

        //....

        fef.SetBinding(Polygon.FillProperty, binding);


1. Why is the binding not working?
2. What do I need to do to make it work?
3. I read somewhere (don't know where) that creating styles and bindings in code (behind) is not really recommended
    - What would be another way to do this?
    - Why is it not recommended?
Community
  • 1
  • 1
Thoms
  • 87
  • 8
  • I would recommend doing it inside xaml since one of the huge point of WPF comparing to win form is to separate out UI and logic code – Steve Jul 27 '16 at 14:03
  • Possible duplicate of [Custom Shaped Button in WPF](http://stackoverflow.com/questions/17532063/custom-shaped-button-in-wpf) – ASh Jul 27 '16 at 14:12
  • @Ash : Your right, the only difference is that I am unsing _Polygon_ instead of _Path_ and _Canvas_ instead of _Grid_ but thats not much of a change. And it works fine with that. However, is there a way to bind the _Path_ or the _Points_ of the _Path_ (so that I can redraw it depending on the _Width_ or _Height_ of the _Button_)? I dont see a _DependencyProperty_ that would fit... Can I create my own? If yes, how is it done? – Thoms Jul 28 '16 at 09:45
  • by the way: if found [this](http://blog.scottlogic.com/2012/04/20/everything-you-wanted-to-know-about-databinding-in-wpf-silverlight-and-wp7-part-two.html) really helpful – Thoms Jul 29 '16 at 14:47

2 Answers2

1

At a very very basic level, binding is done by using the DataContext property of a UI element. For example, if you had a Person class:

public class Person
{
     public string FirstName {get;set;}
     public string LastName {get;set;}
}

You can then use binding to bind these properties to UI elements without explicitly setting them. An example of this would be if you had a StackPanel

<StackPanel x:Name="MyStack" Orientation="Horizontal">
    <TextBlock Text="{Binding FirstName}"/>
    <TextBlock Text="{Binding LastName}"/>
</StackPanel>

This XAML will look for any properties named FirstName or LastName in the DataContext that is set. So by setting the DataContext to a Person object, it can display the data we specify.

MyStack.DataContext = new Person(){ FirstName = "Test", LastName = "Man"};

This will then tell the StackPanel to populate those two textboxes with 'Test' and 'Man'.

This logic extends to any types of UI objects, such as SolidColorBrush for setting colours. If you extended the Person class to accept a SolidColorBrush for a FavColor property, you could then set the background of the panel by setting a binding for that too, i.e.

<StackPanel x:Name="MyStack" Background="{Binding FavColor}" Orientation="Horizontal">

However, as mentioned, you really want to keep your models (the objects that contain the data you want to display, like our Person, or a User, or a Shape, etc) and any sort of View information completely separate. To read more into how to do this, I would look into the Model View View-Model (MVVM) pattern, which focuses heavily on XAML data binding. There are plenty of good tutorials out there, I used video examples to help me get an understanding, so it may be worth searching for these too to expand your knowledge.

plusheen
  • 1,128
  • 1
  • 8
  • 23
1
Binding binding = new Binding();
binding.Path = new PropertyPath("FillProperty");
binding.Source = fillColor;
BindingOperations.SetBinding(hex.polygon, Polygon.FillProperty, binding);

That code creates a binding whose Source is a SolidColorBrush, and whose Path is FillProperty.

What that means is that the binding is going to go to the Source object, and look for a property named FillProperty on that Source object, and then it will pass the value of that property along to the Fill property of a polygon.

That's not what you want: SolidColorBrush has no property named FillProperty (nothing does), and it also doesn't have a property named Fill either. Fill is the target property, not the source property. What you want is this:

hex.polygon.Fill = fillColor;

It's not useful to use a binding in this particular case, because fillColor is a local variable. You use bindings for properties, so that when the source changes, the target will be updated (and/or target changes may updated the source, depending on parameters).

But for the sake of learning how Binding works, this should also do what you want:

Binding binding = new Binding();
binding.Source = fillColor;
BindingOperations.SetBinding(hex.polygon, Polygon.FillProperty, binding);

I see some confusion about Polygon.FillProperty vs Polygon.Fill. Polygon.FillProperty is a public static readonly DependencyProperty object which describes the characteristics of the Polygon's Fill dependency property. Polygon also has a regular property named Fill, which you can use directly as I showed above. It's a convention that the name of the public static readonly DependencyProperty object is the same as the notional dependency property, with a suffix of "Property", but it's not absolutely required.

Community
  • 1
  • 1