Bind Rect Width and Height in xaml
Asked Answered
Z

2

6

I am trying to bind the width and height of a Rect in a ViewPort like this:

<VisualBrush.Viewport>
    <Rect Width="{Binding Path=MyWidth}" Height="{Binding Path=MyHeight}"/>
</VisualBrush.Viewport>

My binding works fine elsewhere but here I get the following error message:

A 'Binding' cannot be set on the 'Width' property of type 'Rect'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

Edit I understand the error message. My question is how to work around it. How do I bind the height and width of the rect?

Zeeba answered 27/1, 2016 at 9:30 Comment(6)
The error message is pretty clear. The properties of the Rect structure aren't bindable, because they aren't dependency properties.Another
So there is no way to do this?Zeeba
VisualBrush.Viewport is a dependency property. You may have a MultiBinding for the Viewport property with a converter that creates a Rect from the two source values.Another
What you can do as a workaround is using a Polygon instead of a Rectangle and bind the PointsProperty to the object. You can still use a rectangle in code but convert it to a points type in a property getter.Beauvais
@NJacobs You can't use a Polygon as value for the Viewport property of a VisualBrush. It's type is Rect.Another
@Another ah yes you are right, my mistake.Beauvais
A
8

Use a MultiBinding like this:

<VisualBrush.Viewport>
    <MultiBinding>
        <MultiBinding.Converter>
            <local:RectConverter/>
        </MultiBinding.Converter>
        <Binding Path="MyWidth"/>
        <Binding Path="MyHeight"/>
    </MultiBinding>
</VisualBrush.Viewport>

with a multi-value converter like this:

public class RectConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return new Rect(0d, 0d, (double)values[0], (double)values[1]);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
Another answered 27/1, 2016 at 10:0 Comment(1)
Thanks, it works. For some reason MultiBinding do not show up as a suggested entry when I start writing it, so I thought it could not be used with the Viewport.Zeeba
F
-2

Updated Answer: Rect is an struct and Height and Width are not dependency properties(see screen shot), so they can't be bound to anything.

enter image description here

Here is a way to do it using Dependency Property and Binding.

MyRect Class with Dependency Properties:

 public class MyRect : DependencyObject,INotifyPropertyChanged
{
    public MyRect()
    {            
        this.Rect = new Rect(0d, 0d, (double)Width, (double)Height);      
    }      

    private Rect rect;

    public Rect Rect
    {
        get { return rect; }
        set 
        {
            rect = value;
            RaiseChange("Rect");
        }
    }

    public double Height
    {
        get { return (double)GetValue(HeightProperty); }
        set { SetValue(HeightProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Height.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HeightProperty =
        DependencyProperty.Register("Height", typeof(double), typeof(MyRect), new UIPropertyMetadata(1d, OnHeightChanged));

    public static void OnHeightChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            var MyRect = (dp as MyRect);
            var hight = Convert.ToDouble(e.NewValue);
            MyRect.Rect = new Rect(0d, 0d, MyRect.Rect.Width, hight);                
        }
    }

    public double Width
    {
        get { return (double)GetValue(WidthProperty); }
        set { SetValue(WidthProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Width.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty WidthProperty =
        DependencyProperty.Register("Width", typeof(double), typeof(MyRect), new UIPropertyMetadata(1d, OnWidthChanged));

    public static void OnWidthChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            var MyRect = (dp as MyRect);
            var width = Convert.ToDouble(e.NewValue);
            MyRect.Rect = new Rect(0d, 0d, width, MyRect.Rect.Height);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaiseChange(string prop)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

}  

View:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {           
        InitializeComponent();
        MyRect = new TabControl.MyRect();            
    }

    private MyRect myRect;
    public MyRect MyRect
    {
        get { return myRect; }
        set { myRect = value; RaiseChange("MyRect");}
    }             

    private void MySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        if (MyRect != null)
        {
            MyRect.Height = e.NewValue/10;
            MyRect.Width = e.NewValue/10;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaiseChange(string prop)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }
}

XAML:

<Window x:Class="TabControl.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
    xmlns:local="clr-namespace:TabControl"
    Title="MainWindow" Height="450" Width="525"       
    DataContext="{Binding RelativeSource={RelativeSource Self}}"        
    >
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="80*"/>
        <RowDefinition Height="80"/>
    </Grid.RowDefinitions>        
    <Rectangle Name="recLogin" Height="300"  Width="400" DataContext="{Binding MyRect}" >
        <Rectangle.Fill>
            <VisualBrush TileMode="None" Viewport="{Binding Rect}">                    
                <VisualBrush.Visual>
                    <ScrollViewer Height="30" Width="100">
                        <Button Content="Transparent" Height="30"  Width="80" />
                    </ScrollViewer>
                </VisualBrush.Visual>
            </VisualBrush>
        </Rectangle.Fill>
    </Rectangle>        
    <Slider Maximum="20" x:Name="MySlider" Value="4" TickFrequency="1" Grid.Row="1"  TickPlacement="TopLeft" ValueChanged="MySlider_ValueChanged" />
</Grid>

Output:

Larger

Smaller

Frame answered 27/1, 2016 at 10:20 Comment(2)
Wow, you've neither read the question nor any of the comments. I'll say it a third time: You can't use anything else than Rect for the value of the VisualBrush.Viewport property.Another
@Another I actually didn't read the question carefully. Thanks for pointing. Learned one thing or two cause of this.Frame

© 2022 - 2024 — McMap. All rights reserved.