lpx

Hi,

I have a problem which i can see some solutions but i don't think they will take advantage of the WPF full power.

I have an application layout which needs an area to represent a screen. I want that area to have the same ratio of the specified resolution of the screen which it represents.

That area is being represented by a Canvas inside a Border which by its turn is with its size properties set to auto, which means that the border is always taking advantage of all the space it has, given by the grid cell.

I want to force that the Canvas will always have the ratio of the resolution even if some space between the border and the canvas will be free.

My approach consisted in gettig the Border size and make the following calculations:

_slideEditor.Width = _workspacePanel.ActualWidth;
_slideEditor.Height = (_workspacePanel.ActualWidth*768)/1024;

The first problem that arrised was the fact that if i resize the grid, the canvas keeps the same. So i need to make this inside an event handler which i don't know which one is the best for this purpose.

I also believe that more experienced users will see a much simpler solution for this problem. Maybe using only XAML

Well, i would love to hear your opinion about this.

Thx,

Best regards,

Nuno




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

Maik Liebing

Hi,

have you tried to bind the height of your canvas to the width using a ValueConverter

for example

Code Snippet

<Canvas Background="Red" Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}, Converter={StaticResource conv}, Mode=OneWay}"/>


now the converter is responsible to keep the ratio of your canvas.

the only problem with this approach is, it only recalculates the height if the width was changed. Resizing your grid vertically has no effect.

but maybe you can improve this




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

lpx

Hi,

I'm trying to use your suggestion but i dont know how to define the converter.

I was trying to use a converter of the type IValueConverter

Code Snippet

<Canvas Background="Red" Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}, Converter=conv, Mode=OneWay}"/>



Code Snippet

public class conv : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
return ((int) value)*0.5;
}
catch (Exception e)
{
}
return null;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}



But it gave me the following error:

{"'IValueConverter' type does not have a public TypeConverter class. Error at Line 11 Position 28."}

Can you guide me here

Thx,

Nuno




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

Maik Liebing

Hi,

the problem is in your binding.

You cannot write

Code Snippet

Converter=conv


since you have to provide an object here.

For example write

Code Snippet

Converter={StaticResource conv}


Now WPF tries to find a resource with the name conv.

For example you can declare your converter instance as a resource of your window

Code Snippet

<Window>
<Window.Resources>
<local:MyConverter x:Key="conv"/>
</Window.Resources>
</Window>


where MyConverter is the name of your class. Dont forget to register the xml-namespace "local" to match your clr-namespace.







Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

lpx

Ok, i got no error but the conversion is not working. I have a bug somewhere, in runtime i have the following error:

System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='<null>' BindingExpressionStick out tongueath=ActualWidth; DataItem='Canvas' (Name=''); target element is 'Canvas' (Name=''); target property is 'Height' (type 'Double')


This is my XAML:

Code Snippet

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:TestBindingResize"
x:Class="TestBindingResize.Window1"
x:Name="Window"
Title="Window1"
Width="640" Height="480">

<Grid x:Name="LayoutRoot">
<Grid.Resources>
<src:Converter x:Key="conv"/>
</Grid.Resources>
<Canvas Background="Red" Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}, Converter={StaticResource conv}, Mode=OneWay}"/>
</Grid>
</Window>



This is my code:

Code Snippet

public class Converter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
Console.WriteLine("o valor e este :" + ((int) value).ToString());

int res = (int) ((int)value*0.5);

Console.WriteLine(res.ToString());

return ((Double) value)*0.5;
}
catch (Exception e)
{
}
return null;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}


The purpose of my code is to give an Height of half the Width. Isnt that right

Whats wrong Can you help me out

Thx,

Nuno




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

Maik Liebing

Hi,

you have an InvalidCastException in your converter which is caught by your try-catch. After that you return null which is not valid, because Height is of type double.

why not just use

public object Convert(object value, Type targetType, object parameter,
CultureInfo culture) {
return ((double) value)*0.5;
}

If you really want the try-catch, ensure you return a compatible value.




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

lpx

Yeah, that's right! Thx!

Can i ask you one more thing I want to limit the height, so i tried to do the following:

Code Snippet

MaxHeight="{Binding ElementName=workspacePanel, Path=ActualHeight}"


Where workspacePanel is the element which contains the panel i want to keep the ratio.

But it's not working.

Is this correct

Binding as so many keyword. How can i get know about them all Everyday i learn a new one.

In the books, this is only sligtly described.

Thx,

Nuno




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

Maik Liebing

Basically this binding should work. Maybe you can post more of your xaml.


BTW: another solution which is more reusable would be to implement an own panel to arrange children and keep ratio.

the xaml could look like
...
<local:RatioPanel WidthHeightRatio="0.5">
<Border .../>
...
</local:RatioPanel>
...

The main advantage would be, it is not important if you resize your window vertically or horizontally, whilte the binding in the previous posts only works with one direction.




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

lpx

Hi,

Can you explain me better this tip

I'm not understanding if WidthHeightRatio is a property already or if i have to create it.

In the code snippet you provided you are examplifying how should i create the control, or instanciating the control

thx,

Nuno




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

Maik Liebing

Hi,

the following example shows a really basic implementation:

Code Snippet

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="TestBindingResize.Window1"
xmlns:local="clr-namespace:TestBindingResize"
x:Name="Window"
Title="Window"
Width="640" Height="500">

<local:RatioPanel WidthHeightRatio="0.5">
<Border Background="Red"/>
</local:RatioPanel>
</Window>



code for the RatioPanel class

Code Snippet

using System;
using System.Windows;
using System.Windows.Controls;

namespace TestBindingResize{
public class RatioPanel : Panel {
#region WidthHeightRatio property

public static readonly DependencyProperty WidthHeightRatioProperty;

public double WidthHeightRatio {
get { return (double) GetValue(WidthHeightRatioProperty); }
set { SetValue(WidthHeightRatioProperty, value); }
}

#endregion

static RatioPanel() {
#region initialisation of WidthHeightRatio property

WidthHeightRatioProperty =
DependencyProperty.Register("WidthHeightRatio", typeof (double),
typeof (RatioPanel),
new FrameworkPropertyMetadata(1d,
FrameworkPropertyMetadataOptions.AffectsMeasure));

#endregion
}

protected override Size MeasureOverride(Size availableSize) {
foreach (UIElement element in InternalChildren) {
element.Measure(availableSize);
}
return availableSize;
}

protected override Size ArrangeOverride(Size finalSize) {

double width = finalSize.Width;
double height = finalSize.Width*WidthHeightRatio;

double scale = Math.Min(1d, finalSize.Height/height);

double newWidth = width*scale;
double newHeight = height*scale;

Rect elementRect =
new Rect((finalSize.Width - newWidth)/2,
(finalSize.Height - newHeight)/2, newWidth, newHeight);

foreach (UIElement element in InternalChildren) {
element.Arrange(elementRect);
}

return finalSize;
}
}
}


As you can see, the panel itself defines the WidthHeightRatio as a dependency property (like the Orientation-Property of a StackPanel). If you add a binding to this property instead of setting a fixed value (in the example a value of 0.5 is set) the panel updates the layout of all children automatically if the binding changes because the property has the AffectsMeasure option set.
The final panel probably should have a better implementation for the MeasureOverride method, but thats not important to show the basic approach.

If you start the example you can see, the red colored border is vertical and horizontal centered inside the panel and keeps its ratio if you resize the window.

You don't have to "instanciate" the control. Just use it as any other wpf control or panel.

Regards
Maik




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

lpx

The example you have show me is new stuff for me.

What should be better in the MeasureOverride method

Thx,

Nuno




Re: Windows Presentation Foundation (WPF) Keeping a determined element with a determined size, automaticly.

Maik Liebing

Hi,

at the moment the panel ignores the desired size of its children. it just calls measure on every children, because its important to call this method during measuring.

Lets assume your RatioPanel is within a grid and you want the cell to be as small as possible (Width of the column and Height of the row set to "Auto"). In this case the result of the measure method is important for the grid to determine the size of the cell.

Or one of your children has a MinWidth or MinHeight constraint.

To get the preferred size of a children you can use its DesiredSize property. Dont use it before you called the children's measure method or you get the preferred size of the last layout pass.

Regards
Maik