Silverlight 4: how to switch control visibility - silverlight

I am using MVVM in my Silverlight app. When control visibility is need to be managed by data, I am connecting its 'Visibility' property to object's corresponding property:
XAML:
<TextBlock Text="Price" Visibility="{Binding PriceVisibility, Mode=OneWay}"/>
<TextBox Text="{Binding TicketPrice, Mode=TwoWay}" Visibility="{Binding PriceVisibility, Mode=OneWay}"/>
CodeBehind (C#):
public string PriceVisibility { get { return PriceVisible ? "Visible" : "Collapsed"; } }
But from my perspective, returning string representation of the Visibility property is not a best approach.
Could you please advise if there are any better way?
Thanks!

I just used Reflector to inspect the type converters in the PresentationFramework.dll
There is already an implementation that can convert between boolean and visibility. You should be able to make use of this in your silverlight application.
public sealed class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool flag = false;
if (value is bool)
{
flag = (bool) value;
}
else if (value is bool?)
{
bool? nullable = (bool?) value;
flag = nullable.HasValue ? nullable.Value : false;
}
return (flag ? Visibility.Visible : Visibility.Collapsed);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((value is Visibility) && (((Visibility) value) == Visibility.Visible));
}
}

I've faced the problem of binding a Boolean value to the visibility property, so I've implemented my own Boolean to Visibility Converter, I'm using it with most of my applications.
Add the Following Class to your application:
public class BoolVisibilityConverter : IValueConverter{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture){
bool isVisible = (bool)value;
return isVisible ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){
System.Windows.Visibility currVisibility = (System.Windows.Visibility)value;
return (currVisibility == System.Windows.Visibility.Visible);
}
}
Now To Use it you'll need to add it as a resource in your XAML Code.
<UserControl.Resources>
<Helpers:BoolVisibilityConverter x:Key="boolVisibilityConverter" />
</UserControl.Resources>
In your example use the following:
<TextBlock Text="Price" Visibility="{Binding PriceVisibility, Mode=OneWay, Converter={StaticResource boolVisibilityConverter}}"/>
<TextBox Text="{Binding TicketPrice, Mode=TwoWay}" Visibility="{Binding PriceVisibility, Mode=OneWay, Converter={StaticResource boolVisibilityConverter}}"/>

Related

CheckBox Binding with Viewmodel property and IConverter not working

I have a simple CheckBox and I want to bind a value from my ViewModel to the IsChecked property of the CheckBox. The value derived from ViewModel is of type sbyte.
I have included a small converter class for that.
However, this code is not working.
Please suggest the right way of doing this.
XAML
<UserControl>
<UserControl.Resources>
<local:TrueFalseConverter x:Key="TFC"/>
</UserControl.Resources>
<CheckBox x:Name="chkOutOfSales" DataContext="{Binding DCIM}" IsChecked="{Binding CurrentRec.Out_of_Sales, Converter={StaticResource TFC},Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</UserControl>
Code Begind converter:
public class TrueFalseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value == true) ? 1 : 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((sbyte)value == 1) ? true : false;
}
}
I found the solution...I had mistyped Out_Of_Sales as Out_of_Sales in my viewmodel...that was the issue...:-).
Thanks for your time..

ComboBox DataTemplate in WPF datagrid with binding not initialized

I have a binding list of Models and a DataGrid. Value can be of couple of data types (bool, double).
public class Model
{
public object Value { get; set; }
}
public void Initialize()
{
var models = new BindingList<Model>();
models.Add(new Model(){ Value = "hello"});
models.Add(new Model(){Value=true});
signals.ItemsSource = models;
}
I want to display the data in the data grid and I want to use textbox for numbers, but combo(true/false) for boolean values.So I implemented bool2string converter and DataTemplateSelector. In my example I have one text column and one template column displaying the same data. When I start the application the combo values are not initialized (nothing is selected). Once I start playing with values, everything works,the values are properly synchronized (if I change value in one column, it will propagate to the other column). Do you have any idea, what mistake I'm doing?
public class BoolToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((bool)value == true) ? "true" : "false";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var comboValue = (value as ComboBoxItem).Content.ToString();
return (String.Compare(comboValue, "true", StringComparison.InvariantCultureIgnoreCase) == 0);
}
}
public class DynamicDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var element = container as FrameworkElement;
var signal = item as Model;
if (element != null && signal != null)
{
if (signal.Value is bool)
return element.FindResource("BoolTemplate") as DataTemplate;
else
return element.FindResource("TextTemplate") as DataTemplate;
}
return null;
}
}
My xaml looks like following:
<Window.Resources>
<comboQuestion:DynamicDataTemplateSelector x:Key="DataTemplateSelector"/>
<comboQuestion:BoolToStringConverter x:Key="BoolToStringConverter"/>
</Window.Resources>
<Grid>
<DataGrid Grid.Row="1" Name="signals" AutoGenerateColumns="False" ItemsSource="{Binding}" >
<DataGrid.Resources>
<DataTemplate x:Key="BoolTemplate">
<ComboBox SelectedItem="{Binding Value, Converter={StaticResource BoolToStringConverter}, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ComboBoxItem>true</ComboBoxItem>
<ComboBoxItem>false</ComboBoxItem>
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="TextTemplate">
<TextBox Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="TextValue" Binding="{Binding Value}"/>
<DataGridTemplateColumn Header="DynamicValue" CellTemplateSelector="{StaticResource DataTemplateSelector}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
EDIT:
I tried to change the SelectedItem to SelectedValue and I also tried to change the Convert part of the converter to:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var cbi = new ComboBoxItem();
cbi.Content = ((bool)value == true) ? "true" : "false";
return cbi;
}
however, the behavior remains the same.
You can convert a boolean to an index number using the following converter:
public class BooleanToIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value == true) ? 0 : 1;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((int)value == 0) ? true : false;
}
}
Then bind to SelectedIndex on the ComboBox:
SelectedIndex="{Binding Value, Converter={StaticResource BooleanToIndexConverter}}

WPF binding button

I have two buttons in my application
Now I want to bind the btnOff to !isOn. Meaning is btnOn is Enabled, btnOff should be Disabled and vice-versa
EDIT: Below is my implementation:
<Button x:Name="btnOn" Content="On" Width="45" Height="24" IsEnabled="{Binding isOn, Converter={StaticResource BoolInverter}}" />
<Button x:Name="btnOff" Content="Off" Width="45" Height="24" IsEnabled="{Binding isOn} />
public class BoolInverterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
return !(bool)value;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
return !(bool)value;
}
return value;
}
}
Wouldn't it be simpler to have another computed/derived property IsOff (no backing field) that negates the IsOn property ?
public bool IsOn{
get{...}
set
{ _isOn = value;
NotifyPropertyChanged("IsOn");
NotifyPropertyChanged("IsOff");
}
}
public bool IsOff
{
get{ return !IsOn;}
}
Converters are normally used to convert data types e.g. to bind a Visibility enum property to a boolean backing property in the viewmodel.
Element data binding,
http://www.codeproject.com/Articles/29054/WPF-Data-Binding-Part-1 -> another example for element data binding

binding to a radio button

I have the following radio button bound to the variable IsAllowed
<RadioButton Name="YesRadioButton" Margin="5,5,0,0" IsChecked="{Binding Path=IsAllowed, Mode=TwoWay}">Yes</RadioButton>
How can I make the No button to take the opposite value only using XAML ?
You do not need to. It will happen by default.
Just make sure that IsAllowed starts off as true, and the rest will take care of its self.
This is because when you click on the No button, it will automatically set the Yes button's checked value (that's how radio buttons work), so the change will happen automatically and you backing class will be updated.
EVEN BETTER: Just use a check box. Yes/no situations are what they are designed for.
There is no Xaml-only solution. You could bind No using a reverse bool Converter though.
<local:NotConverter x:Key="notConverter"/>
{Binding IsAllowed, Mode=TwoWay, Converter=notConverter}
public class NotConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Boolean result = false;
if (value is Boolean)
result = !((Boolean)value);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Boolean result = false;
if (value is Boolean)
result = !((Boolean)value);
return result;
}
}
You will have to write converter using IValueConverter. Here is an example how to do it WPF - Bind to Opposite Boolean Value Using a Converter
The above answer works but I wanted a converter that would apply to both Yes and No radio buttons and reflect the value of nullable booleans. So I made the alternative that takes advantage of converter parameters:
public class YesNoRadioButtonToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return CompareValueWithRequiredValueToBeChecked(value, parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return CompareValueWithRequiredValueToBeChecked(value, parameter);
}
private bool CompareValueWithRequiredValueToBeChecked(object value, object parameterValue)
{
bool? convertedValue = ConvertObjectToBool(value);
bool? convertedParameter = ConvertObjectToBool(parameterValue);
bool result = convertedValue == convertedParameter;
return result;
}
private bool? ConvertObjectToBool(object parameter)
{
string stringResult = parameter == null ? null : parameter.ToString();
bool? convertedResult;
bool convertResultTest = false;
if (stringResult != null && !bool.TryParse(stringResult, out convertResultTest))
{
throw new InvalidCastException(string.Format("Cannot convert {0} to a bool.", parameter));
}
convertedResult = stringResult == null ? (bool?)null : (bool?)convertResultTest;
return convertedResult;
}
}
Here is what the XAML looks like:
<converters:YesNoRadioButtonToBooleanConverter x:Key="yesNoToBool" />
<RadioButton Content="Yes" Name="radYes" GroupName="Group1" IsChecked="{Binding Path=boolProperty1, Mode=TwoWay, Converter={StaticResource yesNoToBool}, ConverterParameter=true}" />
<RadioButton Content="No" Name="radNo" GroupName="Group1" IsChecked="{Binding Path=boolProperty1, Mode=TwoWay, Converter={StaticResource yesNoToBool}, ConverterParameter=false}" />

how to bind a boolean to combobox in wpf

Well I was wondering how to bind a boolean property to a combobox.Combobox will be a yes/no combobox.
You could use a ValueConverter to convert the boolean value to a ComboBox index and back. Like this:
public class BoolToIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value == true) ? 0 : 1;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((int)value == 0) ? true : false;
}
}
}
Assuming Yes is on index 0 and No on index 1. Then you'd have to use that converter in binding to the SelectedIndex property. For this, you declare your converter in your resources section:
<Window.Resources>
<local:BoolToIndexConverter x:Key="boolToIndexConverter" />
</Window.Resources>
Then you use it in your binding:
<ComboBox SelectedIndex="{Binding YourBooleanProperty, Converter={StaticResource boolToIndexConverter}}"/>
I have found myself using the IsSelected property of the ComboBox items for this in the past. This method is entirely in xaml.
<ComboBox>
<ComboBoxItem Content="No" />
<ComboBoxItem Content="Yes" IsSelected="{Binding YourBooleanProperty, Mode=OneWayToSource}" />
</ComboBox>
First solution is to replace your 'Yes/No' combobox with a checkbox because, well, checkbox exists for a reason.
Second solution is to fill your combobox with true and false objects and then bind the 'SelectedItem' of your combobox to your Boolean property.
Here is an example (replace enabled/disabled with yes/no):
<ComboBox SelectedValue="{Binding IsEnabled}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={x:Static converters:EnabledDisabledToBooleanConverter.Instance}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.Items>
<system:Boolean>True</system:Boolean>
<system:Boolean>False</system:Boolean>
</ComboBox.Items>
</ComboBox>
Here is Converter:
public class EnabledDisabledToBooleanConverter : IValueConverter
{
private const string EnabledText = "Enabled";
private const string DisabledText = "Disabled";
public static readonly EnabledDisabledToBooleanConverter Instance = new EnabledDisabledToBooleanConverter();
private EnabledDisabledToBooleanConverter()
{
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Equals(true, value)
? EnabledText
: DisabledText;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//Actually won't be used, but in case you need that
return Equals(value, EnabledText);
}
}
And no need to play with indices.

Resources