Xamarin Forms RowDefinition height binding via converter set height autoD - c#

Currently I would like to bind my Height property on a Rowdefinition in a Grid. I want to show the row if the property IsOnline on my ViewModel is set to true.
Binding a number as Height is no problem at all, I am just wondering how I could bind it to Auto.
My View:
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="{Binding IsOnline, Converter={StaticResource HeightConverter}}"/>
</Grid.RowDefinitions>
My Converter HeightConverter:
public class HeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
{
if ((bool)value)
{
return "Auto";
}
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Check GridLength Struct it has properties like Auto, Star and etc. You could use those as return values from the IValueConverter.

Related

How to get new window height after resize in xaml

I have a UserControl that I've initialized with ResizeMode=CanResize but I need certain elements within the UserControl to depend on the window's height after the user resizes it by dragging the sides. Specifically, I need to the size of a TextBox to always have height that's 40 less than the window's height.
So how do I get this new window size after the user resizes?
Thanks!
NOTE:
Thanks to Pavel Anikhousk, using ActualHeight works. For future reference, this was my code:
<RowDefinition Height="{Binding ActualHeight, Converter={convs:ChatTextBoxSizeConverter}}" />
and ChatTextBoxSizeConverter.cs
namespace Converters
{
[ValueConversion(typeof(string), typeof(string))]
public class ChatTextBoxSizeConverter : MarkupExtension, IValueConverter
{
private static ChatTextBoxSizeConverter instance_;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (System.Convert.ToInt32(value) - 40);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return instance_ ?? (instance_ = new ChatTextBoxSizeConverter());
}
}
}
I'm not sure about how your layout is, but if you want to keep your window responsive and well designed this could be it:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<TextBox />
</Grid>
Note the TextBox will fill the entire space, but a 40px is always reserved at the bottom.

How to bind UI color attribute with class property

I have a user control which is an ellipse that acts like a "led". I want to bind its "Fill" to a boolean property (State).
I used for that a boolean to Color converter.
here is the user control I did:
<UserControl x:Class="Sol.Components.Led"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...
xmlns:conv="clr-namespace:Sol.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Ellipse Fill="{Binding Converter={StaticResource BoolToColor}}" StrokeThickness="3" Stroke="Gray"/>
</Grid>
</UserControl>
also the converter is not recognised in the user control! I did it like this
public class BoolToColor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
if ((bool)value == true)
return Colors.Green; // to replace with onColor
else
return Colors.Red; // to replace with offColor
}
return Colors.LightGray;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Color)
{
if ((Color)value == Colors.Green) // to compare with onColor
{
return true;
}
}
return false;
}
}
I used a window to include 4 user contols:
<Window x:Class="Sol.Menu.Leds"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:led="clr-namespace:Sol.Components"
xmlns:conv="clr-namespace:Sol.Converters"
Title="Leds" Height="300" Width="300">
<Window.Resources>
<conv:BoolToColor x:Key="BoolToColor" />
</Window.Resources>
<Grid>
...
<led:Led Grid.Column="0" Grid.Row="0" x:Name="led1" State="False"/>
<led:Led Grid.Column="0" Grid.Row="1" x:Name="led2" State="False"/>
<led:Led Grid.Column="1" Grid.Row="0" x:Name="led3" State="False"/>
<led:Led Grid.Column="1" Grid.Row="1" x:Name="led4" State="False"/>
</Grid>
</Window>
and the used control class :
public partial class Led : UserControl
{
private bool state;
public bool State
{
get { return state; }
set { state = value; }
}
private Color onColor;
public Color OnColor
{
get { return onColor; }
set { onColor = value; }
}
private Color offColor;
public Color OffColor
{
get { return offColor; }
set { offColor = value; }
}
public Led()
{
InitializeComponent();
}
}
this is works without binding and the window shows 4 ellipses, but I am unable to change the color dynamically (from the code bedhind).
any help to fix the binding?
Try to bind to the State property of the UserControl:
<Ellipse Fill="{Binding Path=State,
RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource BoolToColor}}" StrokeThickness="3" Stroke="Gray"/>
You should also return a Brush instead of a Color from your converter:
public class BoolToColor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
if ((bool)value == true)
return Brushes.Green; // to replace with onColor
else
return Brushes.Red; // to replace with offColor
}
return Brushes.LightGray;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Brush)
{
if ((Brush)value == Brushes.Green) // to compare with onColor
{
return true;
}
}
return false;
}
}
You need to implement PropertyChanged so the UI knows a property has been changed.
Read here how it should be done: https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-property-change-notification
Fill, like all other UI "color" properties, is actually a Brush value, not a Color
Change your converter to return Brushes.Red / Brushes.Green.

Is there an IValueConverter that does if-then

What I am trying to do:
<Grid>
<Grid.RowDefinitions>
...
<!--The next line is pseudo code for what I am trying to achieve-->
<RowDefintion Height="if(EditEnabled) { 10* } else { 0 }" />
...
</Grid.RowDefinition>
...
<DockPanel Visibility="{Binding EditEnabled, Converter={StaticResource InverseBooleanToVisibilityConverter}}" ...>
...
I am trying to change the visibility of the DockPanel depending on whether editing is enabled, while keeping he ability to resize and have fixed heights and relative heights.
The question:
Is there an IValueConverter (System.Windows.Data.IValueConverter) that can take a boolean, and two numbers and choose one of the GridLengths based on the boolean? From just inspecting the interface of IValueConverter it doesn't look like this is quite the right type to use.
Or is there a better way to inject the GridLength that I want?
What I have tried:
Looking through the inheritors of IValueConverter - nothing obvious to me
Moving the Height="10*" inside the DockPanel tag and changing the RowDefinition to be Auto - this created an conversion exception
Searching here
Unfortunately, there is no IValueConverter that does if-then.
(and to be more specific: you can not do if-then logic with the XAML)
But you can do the if-then logic in the C# code.
here is the solution
public class HeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool enableEdit = (bool)value;
double param = System.Convert.ToDouble(parameter);
if (enableEdit)
return new GridLength(param, GridUnitType.Star);
else
return new GridLength(0);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
and the window like this.
<Window.Resources>
<local:HeightConverter x:Key="heightConverter"/>
<sys:Int32 x:Key="param">10</sys:Int32>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="{Binding Path=EditEnabled, Converter={StaticResource heightConverter}, ConverterParameter={StaticResource param}}" />
</Grid.RowDefinitions>
</Grid>
please consider also define the required namespace that you will use, like the following
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:[your namespace]"
Update the same result could be achieved by using IMutliValueConverter
public class HeightMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool enableEdit = (bool)values[0];
double param = System.Convert.ToDouble(values[1]);
if (enableEdit)
return new GridLength(param, GridUnitType.Star);
else
return new GridLength(0);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
and the window like this
<Window.Resources>
<local:HeightMultiConverter x:Key="heightMutliConverter"/>
<sys:Int32 x:Key="param">10</sys:Int32>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition >
<RowDefinition.Height>
<MultiBinding Converter="{StaticResource heightMutliConverter}">
<Binding Path="EditEnabled"/>
<Binding Source="{StaticResource param}"/>
</MultiBinding>
</RowDefinition.Height>
</RowDefinition>
</Grid.RowDefinitions>
</Grid>
NOTE: just do not forget, you have to take care of the Source by setting the DataContext property.
There is a built-in converter you may be able to use: AlternationConverter. You specify a list of values (of arbitrary type), bind to an integer, and the converter looks up the integer in the list of values (modulo the value count).
If you specify two values for this AlternationConverter, and you're able to provide your EditEnabled property as an integer 0 or 1, then you can map that 0 and 1 to any value you want.
If you feel it doesn't make sense to convert your bool to an integer first (something I can sympathise with), you could still use AlternationConverter as inspiration for a custom converter that doesn't require the model value to be of type int.
Create a BooleanConverter<T> base class as described in http://stackoverflow.com/a/5182660/469708
public class BooleanConverter<T> : IValueConverter
{
public BooleanConverter(T trueValue, T falseValue)
{
True = trueValue;
False = falseValue;
}
public T True { get; set; }
public T False { get; set; }
public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool && ((bool) value) ? True : False;
}
public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is T && EqualityComparer<T>.Default.Equals((T) value, True);
}
}
Then write
public class BooleanToGridLengthConverter : BooleanConverter<System.Windows.GridLength>
{
public BooleanToGridLengthConverter() : base(
new System.Windows.GridLength(1, System.Windows.GridUnitType.Star),
new System.Windows.GridLength(0))
{
}
}
The values for true and false can be set directly, no need for a MultiValueConverter (as long as only the boolean parameter needs to be bindable).
<convert:BooleanToGridLengthConverter x:Key="Converter" True="10*" False="0" />
Alternatively, you can derive the converter from MarkupExtension and directly use it like this:
<RowDefinition Height="{Binding EditEnabled, Converter={convert:BooleanToGridLengthConverter True=10*, False=0}" />

Why would I get a "BoolToRowHeightConverter is not supported in a Windows Presentation Foundation(WPF) project error in xaml?

Why would I get a "BoolToRowHeightConverter is not supported in a Windows Presentation Foundation(WPF) project error in xaml?
I was using a converter to convert rowheight to * and Auto in a grid based on the expander's IsExpanded property.
Code in xaml:
<RowDefinition Height="{Binding IsExpanded, ElementName=Expander5, Converter={x:Static BoolToRowHeightConverter.Instance}}"/>
Code in xaml.cs:
public class BoolToRowHeightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((bool)value) return new GridLength(1, GridUnitType.Star);
else
return GridLength.Auto;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Typically, IValueConverters are used like this:
a) Add a namespace in your XAML page that references your converter class... usually it looks something like this:
xmlns:Converters="clr-namespace:WpfApplication1.Converters"
b) Add an instance of your converter class into the Resources section of your page (or of App.xaml:
<Window.Resources>
<Converters:BoolToRowHeightConverter x:Key="BoolToRowHeightConverter" />
...
</Window.Resources>
c) Reference your converter instance by the x:Key value that you gave it:
<RowDefinition Height="{Binding IsExpanded, ElementName=Expander5,
Converter={StaticResource BoolToRowHeightConverter}}" />
You have decided to reference the value converter by using the x:Static markup extension ({x:Static BoolToRowHeightConverter.Instance}) but then you also need to provide the actual field or property that you reference (Instance). To do that you need to add it to the BoolToRowHeightConverter class:
public class BoolToRowHeightConverter : IValueConverter
{
// Convert and ConvertBack methods ...
public static readonly BoolToRowHeightConverter Instance = new BoolToRowHeightConverter();
}

IValueConverter in ResourceDictionary

I'm trying to use a Converter inside a ResourceDictionary. That's the code I have:
<Window x:Class="Metro.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cnv="clr-namespace:Metro.converters">
<Window.Resources>
<cnv:DarkenColorConverter x:Key="Darken" />
<Color x:Key="Red">#FF0000</Color>
<SolidColorBrush Color="{StaticResource Red}"
x:Key="Accent" />
<SolidColorBrush Color="{Binding Source={StaticResource Red}, Converter={StaticResource ResourceKey=Darken}}"
x:Key="DarkAccent" />
</Window.Resources>
<StackPanel>
<Grid Background="{StaticResource Accent}">
<TextBlock>grid 1</TextBlock>
</Grid>
<Grid Background="{StaticResource DarkAccent}">
<TextBlock>grid 2</TextBlock>
</Grid>
</StackPanel>
</Window>
Here's the converter:
public class DarkenColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Brushes.Blue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Brushes.Gray;
}
}
But somehow it's not working. As soon as I use the converter inside the Grid directly, everything works fine:
<Grid Background="{Binding Source={StaticResource Red}, Converter={StaticResource ResourceKey=Darken}}">
<TextBlock>grid 2</TextBlock>
</Grid>
What`s wrong with the first xaml sample?
In the first conversion you are converting a Color, the one in the Grid is converting a SolidColorBrush.
You will have to modify your converter to accept Color also.
public class DarkenColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double percentage = 0.8;
if (parameter != null)
{
double.TryParse(parameter.ToString(), out percentage);
}
if (value is SolidColorBrush)
{
Color color = (value as SolidColorBrush).Color;
return new SolidColorBrush(Color.FromRgb((byte)(color.R * percentage), (byte)(color.G * percentage), (byte)(color.B * percentage)));
}
else if (value is Color)
{
Color color = (Color)value;
return Color.FromRgb((byte)(color.R * percentage), (byte)(color.G * percentage), (byte)(color.B * percentage));
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
The problem was the wrong converter return type.
Working converter:
public class DarkenColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Colors.Blue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Colors.Gray;
}
}

Categories

Resources