Binding a Distinction basedon Actual Width to Visibility - wpf

Is it possible to bind an ActualWidth or Width property of a Control to the Visibility of another Control with a distinction about value (like <200)? In my Opinion it is only possible with a converter because a DataTrigger can not work with > or <.
So I tried it with a Converter but it didn't work. I'm not sure which BindingMode is necessary and which kind of converter I need for such a solution.
The xaml code:
<StackPanel>
<Slider x:Name="slider" Height="36" Width="220" Maximum="500"/>
<Rectangle x:Name="mover" Height="12" Stroke="Black" Width="{Binding Value, ElementName=slider}"/>
<Rectangle x:Name="rectangle" Fill="#FFFF9E0E" Height="34" Width="112" Visibility="{Binding ActualWidth, Converter={StaticResource umkehr}, ElementName=rectangle, Mode=OneWay}"/>
</StackPanel>
and the idea for the converter:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null) {
var val = System.Convert.ToDouble(value);
if (val > 100)
return Visibility.Visible;
return Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}

It is likely not working because you are binding your Rectangle's Visibility to the same rectangle's ActualWidth, and an invisible rectangle will always have a width of 0, so will never be visible.
Here's some examples. One binds to the other rectangle's ActualWidth, and the other binds to your Slider's Value
<Rectangle x:Name="rectangle"
Visibility="{Binding ActualWidth, ElementName=mover,
Converter={StaticResource umkehr}}"/>
or
<Rectangle x:Name="rectangle"
Visibility="{Binding Value, ElementName=slider,
Converter={StaticResource umkehr}}"/>
And as far as I know, there's no easy way of basing a value off of if something is greater than or less than a value. Coverters are your best option.

ActualWidth is a readonly property exposed by FrameworkElement class -
public double ActualWidth { get; }
It is get only property hence you can't set it to other value from code. You can bind to Width of your control instead to make it work.
EDIT
This works for me, may be this is what you want -
Converter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double)
{
return ((double)value > 100) ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
XAML
<StackPanel>
<Slider x:Name="slider" Height="36" Width="220" Maximum="500"/>
<Rectangle x:Name="mover" Height="12" Stroke="Black" Width="{Binding Value, ElementName=slider}"/>
<Rectangle x:Name="rectangle" Fill="#FFFF9E0E" Height="34" Width="112" Visibility="{Binding ActualWidth, Converter={StaticResource MyConverter}, ElementName=mover, Mode=OneWay}"/>
</StackPanel>

If you're attempting to change the Visibility of a control based on the ActualWidth of another control, you'll either need to use a IValueConverter or you're own type of MarkupExtension (inherit from Binding or BindingBase).
Converter Option:
[ValueConversion(typeof(Double), typeof(Visibility))]
[ValueConversion(typeof(Double?), typeof(Visibility))]
public class MinimumLengthToVisibilityConverter : IValueConverter
{
public Double MinLength { get; set; }
public override Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture)
{
if ((value == null) || !(value is Double))
{
return DependencyProperty.UnsetValue;
}
return (((Double)value) > MinLength) ? Visibility.Visible : Visibility.Collapsed;
}
public override Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
MarkupExtension Option:
Read this blog post to get a better feel for how to implement this...

You can actually have the value in a parameter, so you can re-use the converter if you need to:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double cutoff = 0.0;
if(parameter is double)
{
cutoff = (double)parameter;
}
if (parameter is string)
{
Double.TryParse(parameter.ToString(), out cutoff);
}
if (value is double)
{
return ((double)value > cutoff) ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
And the XAML:
<StackPanel>
<Slider x:Name="slider" Height="36" Width="220" Maximum="500"/>
<Rectangle x:Name="mover" Height="12" Stroke="Black" Width="{Binding Value, ElementName=slider}"/>
<Rectangle x:Name="rectangle" Fill="#FFFF9E0E" Height="34" Width="112"
Visibility="{Binding ActualWidth, Converter={StaticResource ActualWidthToVisibilityConverter},
ElementName=mover, Mode=OneWay, ConverterParameter=100}"/>
</StackPanel>

Related

WPF textbox expression bindings

I have two textboxes bound to a slider. I want the second textbox to be 1.2 time the value of the slide.
<Slider Maximum="500" Minimum="25" TickFrequency="5" IsSnapToTickEnabled="True" Name="slCellHeight" />
<TextBox Text="{Binding ElementName=slCellHeight, Path=Value, UpdateSourceTrigger=PropertyChanged}" Width="40" Name="txtCellHeight" />
<TextBox Text="{Binding ElementName=slCellHeight, Path=Value, UpdateSourceTrigger=PropertyChanged}" Width="40" Name="txtCellWidth" />
That is, when the slider shows 100, the first textbox(txtCellHeight) should show 100. This is working fine. I want to the second one to be 120.
I tried the calBinding but no success. Please suggest some good ways to do that.
Use a converter.
XAML:
<Window.Resources>
<local:MultiplyConverter x:Key="MultiplyConverter" />
</Window.Resources>
...
<TextBox Text="{Binding ElementName=slCellHeight, Path=Value,
UpdateSourceTrigger=PropertyChanged, Converter={StaticResource
MultiplyConverter}, ConverterParameter=1.2 }" Width="40" Name="txtCellWidth" />
Class MultiplyConverter:
class MultiplyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double result;
double.TryParse(parameter.ToString(), out result);
double mult = (double)value * result;
return mult;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I've done it on the fly, so maybe it will need some fixes to compile.

Bind IsEnabled value from string

How can I bind a value of string Y or N to a isEnabled Value?
<TextBox IsEnabled="{Binding Path=StringValueFromSomeEntity, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
StringValueFromSomeEntity can be a Y or N value.
Use a converter to convert the string to a bool-value:
public class StringToBoolConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.ToString().ToLower() == "y")
return true;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
return "Y";
return "N";
}
}
Refer to it in the resources:
<Window.Resources>
<conv:StringToBoolConverter x:Key="StringToBool"/>
</Window.Resources>
Apply it to your binding (if you just want to change the IsEnabled property according to your string, use Mode=OneWay, but if you really want to bind TwoWay you need the ConvertBack-method):
<TextBox IsEnabled="{Binding Path=StringValueFromSomeEntity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ResourceKey=StringToBool}"/>
You can create an IValueConverter subclass like this:
public class YNBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value as string) == 'Y';
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value ? 'Y' : 'N';
}
}
You need ConvertBack if you are expecting to bind TwoWay.
Then add it to the resources of your page.
And add it to the binding
{Binding Path=StringValueFromSomeEntity, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged, Converter={StaticResource YNBoolConverter}}
By default string cann't be converted into Boolen type so you have to tell WPF how to convert and take the value in place where you want to have.
here are two ways to implement this.
Using ValueConverter (prefered way)
Add a Converter into your project like below.
public class StringToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string val = System.Convert.ToString(value).ToUpper();
if (string.IsNullOrWhiteSpace(val))
return false;
return val == "Y" ? true : false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add the namespace into you window and add resource
<Window.Resources>
<convrter:StringToBoolConverter x:Key="stringtoboolConverter"/>
</Window.Resources>
Now refrance this convert into IsEnabled Propery of Checkbox.
<GroupBox Header="With Converter" >
<StackPanel>
<TextBox x:Name="txt1" Text="Y" />
<CheckBox IsEnabled="{Binding ElementName=txt1,Path=Text,Converter={StaticResource stringtoboolConverter}}" />
</StackPanel>
</GroupBox>
Using Style / Triggers (alternative way)
<TextBox x:Name="txt" Text="Y" />
<CheckBox Content="IsEnabled" Tag="{Binding ElementName=txt,Path=Text}" >
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<Trigger Property="Tag" Value="Y" >
<Setter Property="IsEnabled" Value="true"/>
</Trigger>
<Trigger Property="Tag" Value="N" >
<Setter Property="IsEnabled" Value="false"/>
</Trigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Use a ValueConverter.
Create a class which implements IValueConverter
for example
public class StringToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string value = (string)value;
return value.Trim().ToLower() == "y";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
After that you can it applay to your binding

Disable scrolling if the cursor is over element

I wanted to disable the scrolling when the cursor is over an element in WPF.
If the mouse is over the red rectangle, the scroll should be disabled.
The red rectangle is on a scrollviewer.
Any idea?
I got it to work with this code:
<Window.Resources>
<converter:MouseOverToScrollBarVisibility x:Key="scrollVisibility" />
</Window.Resources>
<ScrollViewer VerticalScrollBarVisibility="{Binding IsMouseOver,
ElementName=rec,
Converter={StaticResource scrollVisibility}}">
<Rectangle Height="50" Width="50" Fill="Red" x:Name="rec"/>
</ScrollViewer>
Then just define this converter:
[ValueConversion(typeof(bool), typeof(ScrollBarVisibility))]
sealed class MouseOverToScrollBarVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((bool)value) ? ScrollBarVisibility.Hidden : ScrollBarVisibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}

binding radiobuttons group to a property in WPF

Let's imagine that I have:
<RadioButton GroupName="Group1" IsChecked="{Binding Path=RadioButton1IsChecked}" />
<RadioButton GroupName="Group1" IsChecked="{Binding Path=RadioButton2IsChecked}" />
And then in my data source class I have:
public bool RadioButton1IsChecked { get; set; }
public bool RadioButton2IsChecked { get; set; }
public enum RadioButtons { RadioButton1, RadioButton2, None }
public RadioButtons SelectedRadioButton
{
get
{
if (this.RadioButtonIsChecked)
return RadioButtons.RadioButton1;
else if (this.RadioButtonIsChecked)
return RadioButtons.RadioButton2;
else
return RadioButtons.None;
}
}
Can I somehow bind my radio buttons directly to SelectedRadioButton property? I really need RadioButton1IsChecked and RadioButton2IsChecked properties only to calculate the selected radiobutton.
Declare an enumeration similar to:
enum RadioOptions {Option1, Option2}
XAML:
<RadioButton IsChecked="{Binding SelectedOption, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:RadioOptions.Option1}}"/>
<RadioButton IsChecked="{Binding SelectedOption, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static local:RadioOptions.Option2}}"/>
Converter class:
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value) ? parameter : Binding.DoNothing;
}
}
<RadioButton GroupName="Group1" IsChecked="{Binding Path=SelectedRadioButton, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=RadioButton1}" />
<RadioButton GroupName="Group1" IsChecked="{Binding Path=SelectedRadioButton, Converter={StaticResource EnumBooleanConverter}, ConverterParameter=RadioButton2}" />
public enum RadioButtons { RadioButton1, RadioButton2, None }
public RadioButtons SelectedRadioButton {get;set;}
public class EnumBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var ParameterString = parameter as string;
if (ParameterString == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object paramvalue = Enum.Parse(value.GetType(), ParameterString);
return paramvalue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var ParameterString = parameter as string;
var valueAsBool = (bool) value;
if (ParameterString == null || !valueAsBool)
{
try
{
return Enum.Parse(targetType, "0");
}
catch (Exception)
{
return DependencyProperty.UnsetValue;
}
}
return Enum.Parse(targetType, ParameterString);
}
}
We can create the radio buttons dynamically, ListBox can help us do that, without converters, quite simple.
The advantage is below:
if someday your enum class changes, you do not need to update the GUI (XAML file).
The steps are below:
create a ListBox and set the ItemsSource for the listbox as the enum and binding the SelectedItem of the ListBox to the Selected property.
Then the Radio Buttons for each ListBoxItem will be created.
public enum RadioButtons
{
RadioButton1,
RadioButton2,
None
}
Step 1: add the enum to static resources for your Window, UserControl or Grid etc.
<Window.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type system:Enum}"
x:Key="RadioButtons">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:RadioButtons" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Step 2: Use the List Box and Control Template to populate each item inside as Radio button
<ListBox ItemsSource="{Binding Source={StaticResource RadioButtons}}" SelectedItem="{Binding SelectedRadioButton, Mode=TwoWay}" >
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<RadioButton
Content="{TemplateBinding ContentPresenter.Content}"
IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource TemplatedParent},
Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
Now, enjoy~
References:
https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/
XAML:
<RadioButton IsChecked="{Binding Path=SelectedOption, UpdateSourceTrigger=PropertyChanged}">Option1</RadioButton>
<RadioButton IsChecked="{Binding Path=SelectedOption, UpdateSourceTrigger=PropertyChanged, Converter={v:NotBoolenConverter}}">Option2</RadioButton>
Converter:
public class NotBoolenConverter : IValueConverter
{
public NotBoolenConverter()
{
}
public override object Convert(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
bool output = (bool)value;
return !output;
}
public override object ConvertBack(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
bool output = (bool)value;
return !output;
}
}
Works with 2 radio buttons, by binding one to the opposite of the other.

WPF ComboBox a better way to format ItemsSource

Morning Guys,
I have a few ComboBoxes bound to List of TimeSpan. I am formatting the TimeSpans using IValueConverter and ItemTemplate. I was wondering if there were an easier way to format the TimeSpans. Here's what I'm currently doing.
public class TimeSpanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return string.Empty;
TimeSpan t = TimeSpan.MinValue;
TimeSpan.TryParse(value.ToString(), out t);
return "{0:00}:{1:00}".F(t.Hours,t.Minutes);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return TimeSpan.Parse(value.ToString());
}
#endregion
}
<Canvas>
<Canvas.Resources>
<bc:TimeSpanConverter x:Key="ts" />
<DataTemplate x:Key="TimeSpanTemplate">
<TextBlock Text="{Binding ., Converter={StaticResource ts}}" />
</DataTemplate>
</Canvas.Resources>
<TextBlock Canvas.Left="6"
Canvas.Top="6"
Height="21"
Name="textBlock4"
Text="Begin"
Width="40" />
<TextBlock Canvas.Left="81"
Canvas.Top="6"
Height="21"
Name="textBlock5"
Text="End"
Width="40" />
<ComboBox Canvas.Left="7"
Canvas.Top="25"
Height="23"
Name="TimeBeginCombo"
ItemTemplate="{StaticResource TimeSpanTemplate}"
SelectedItem="{Binding TimeBegin}"
Width="68" />
<ComboBox Canvas.Left="81"
Canvas.Top="25"
Height="23"
Name="TimeEndCombo"
ItemTemplate="{StaticResource TimeSpanTemplate}"
SelectedItem="{Binding TimeEnd}"
Width="68" />
</Canvas>
</GroupBox>
Is what you're binding to of type TimeSpan? If so, then the converter can be much simpler
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is TimeSpan))
return string.Empty;
TimeSpan t = (TimeSpan)value;
return "{0:00}:{1:00}".F(t.Hours,t.Minutes);
}
And also, a general remark - are you sure you need to layout your UI on a Canvas and using absolute coordinates? :)

Resources