Toggling a bound radio button by code - c#

WPF / Caliburn micro related question.
I have 4 radio buttons that I am binding the IsChecked property to ArrowType, which has a type of LogicArrowEnum enumeration that I created.
Radiobuttons use a converter to properly assign the relevant enumeration to the ArrowType property based on which button has been clicked.
XAML:
<Window.Resources>
<my:EnumToBoolConverter x:Key="EBConverter"/>
</Window.Resources>
...
<RadioButton IsChecked="{Binding ArrowType,
Converter={StaticResource EBConverter},
ConverterParameter={x:Static my:LogicArrowEnum.ARROW}}"
Name="LogicArrow"
Style="{StaticResource {x:Type ToggleButton}}"
Width="50"
<TextBlock Text="Arrow"/>
</RadioButton>
<RadioButton IsChecked="{Binding ArrowType,
Converter={StaticResource EBConverter},
ConverterParameter={x:Static my:LogicArrowEnum.ASSIGN}}"
Name="LogicAssign"
Style="{StaticResource {x:Type ToggleButton}}"
Width="50"
<TextBlock Text="Assign"/>
</RadioButton>
<RadioButton
IsChecked="{Binding ArrowType,
Converter={StaticResource EBConverter},
ConverterParameter={x:Static my:LogicArrowEnum.IF}}"
Name="LogicIf"
Style="{StaticResource {x:Type ToggleButton}}"
Width="50"
<TextBlock Text="If" />
Code:
public class EnumToBoolConverter : IValueConverter
{
public object Convert(object value,
Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (parameter.Equals(value))
return true;
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return parameter;
}
}
public enum LogicArrowEnum
{
ARROW = 1,
ASSIGN = 2,
IF = 3,
IF_ELSE = 4
}
public LogicArrowEnum ArrowType
{
get { return arrowType; }
set
{
arrowType = value;
NotifyOfPropertyChange(() => ArrowType);
}
}
The code works wonderfully - user clicks a button, the ArrowType property is properly bound.
I'd like to make this work backwards as well. For example, if I set the ArrowType property to LogicArrowEnum.ASSIGN via code, the UI should show that the Assign button has been toggled. For some reason, this does not work as expected. Inside the set method of property, whenever I assign the ArrowType property to an arbitrary enumeration, the private field of arrowType is first assigned as the value that I want it to, but then as soon as the code reaches NotifyOfPropertyChange method, it makes it enter the set method again but then resets the arrowType private field to the previously toggled button.
Is this a Caliburn Micro related bug or some WPF related bug? How can I fix this?

There is no relation to Caliburn.Micro.
Check out this: How to bind RadioButtons to an enum?
Take a converter from Scott answer and it will work fine.

Related

ToggleButton bound to Enum, how do you prevent toggling off?

I have a group of ribbon toggle buttons inside of the same container in my XAML like this:
<ribbon:RibbonGroup Header="Layouts">
<ribbon:RibbonToggleButton Label="One"
IsChecked="{Binding PaneManager.Layout,
Converter={StaticResource EnumToBooleanConverter},
ConverterParameter={s:Static windows:Layouts.One}}"/>
<ribbon:RibbonToggleButton Label="Two Vertical"
IsChecked="{Binding PaneManager.Layout,
Converter={StaticResource EnumToBooleanConverter},
ConverterParameter={s:Static windows:Layouts.TwoVertical}}"/>
<!-- etc. for 2 horizontal and 4 panes -->
</ribbon:RibbonGroup>
I'm using the same EnumToBooleanConverter outlined in this answer:
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
The problem is that when a user clicks the toggle button that's already selected, the toggle button happily turns itself off--even though the bound value matches that toggle button. When tracing through the code, what happens is that the ConvertBack method is called and it returns Binding.DoNothing. Afterwards, Convert is not called to reassign the value. When ConvertBack returns a value (i.e. it is clicked a second time and IsChecked is true again), the Convert method is called to reassign the value.
If I change the return type on false to be DependencyObject.UnsetValue, the toggle button is still turned off but now it has a red outline.
How do I force WPF to re-evaluate the bound value so that the toggle button stays on?
OK, it seems I have to control whether the toggle button IsHitTestVisible to make it behave properly. If I change my toggle buttons to look like this:
<ribbon:RibbonGroup Header="Layouts">
<ribbon:RibbonToggleButton Label="One"
IsChecked="{Binding PaneManager.Layout,
Converter={StaticResource EnumToBooleanConverter},
ConverterParameter={s:Static windows:Layouts.One}}"
IsHitTestVisible="{Binding IsChecked,
RelativeSource={RelativeSource Self},
Converter={StaticResource InverseBooleanConverter}}"/>
<ribbon:RibbonToggleButton Label="Two Vertical"
IsChecked="{Binding PaneManager.Layout,
Converter={StaticResource EnumToBooleanConverter},
ConverterParameter={s:Static windows:Layouts.TwoVertical}}"
IsHitTestVisible="{Binding IsChecked,
RelativeSource={RelativeSource Self},
Converter={StaticResource InverseBooleanConverter}}"/>
<!-- etc. for 2 horizontal and 4 panes -->
</ribbon:RibbonGroup>
And then supply the following converter:
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool)value;
}
}
It feels like a hack, but it does prevent toggling back off. I am seriously open to more elegant solutions.

Binding items in ListBox in WP8

In a Windows Phone 8 app,
I have a listbox with 2 TextBlocks and a button.
I have a list of 2 strings and a boolean & I am able to bind the strings to the TextBlocks.
<ListBox Name="ListboxTest">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" TextWrapping="Wrap"/>
<TextBlock Text="{Binding Value}" TextWrapping="Wrap"/>
<Button />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And this is the C# code to bind to the list box.
public class Detail
{
public string Key { get; set; }
public string Value { get; set; }
public bool check { get; set; }
}
public List<Detail> ClassList = new List<Detail>();
ListboxTest.ItemsSource = ClassList;
I want to display the button only when the boolean value is true.
How do I do it?
Take a look at this. Actually what you really need is a Converter by implementing the IValueConverter. This is also a good example where you could read about it. Bind the boolean value with the visibility property of the button and you are done! ;)
You can use boolean to visibility converter to hide, show button
Here are example:
public class BoolToVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var boolValue = false;
if (value != null) boolValue = (bool)value;
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
In app.xaml
<my:BoolToVisibility x:Key="BoolToVisibility"/>
In your data template
<Button Visibility="{Binding Path=YourBoolProperty,Converter={StaticResource BoolToVisibility}}>
Or, you could add this property to the Detail class:
public Visibility ButtonVisibility {
get {
return this.check == true ? Visibility.Visible : Visibility.Collapsed;
}
}
And then just bind the button's Visibility to the ButtonVisibility property without any converters.
<Button Visibility="{Binding ButtonVisibility}" />
Please try those use Triggers.
Various Triggers in windows phone Msdn.
Please use ObservableCollection in WP8 for binding instead of List.
Please make your properties are implemented with INotifyPropertyChanged If your Boolean property is not implemented with inotifypropertychanged the view will not know the value is changed.hence the Data trigger will not work.
Namespace
xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ec="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
x:Class="XXX_XXXX"
<Button Content="My button"
Stretch="None"
HorizontalAlignment="Stretch"
VerticalAlignment="Top">
<interactivity:Interaction.Triggers>
<ec:DataTrigger Binding="{Binding Check}" Value="True">
<ec:ChangePropertyAction PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Visible</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</ec:DataTrigger>
<ec:DataTrigger Binding="{Binding Check}" Value="False">
<ec:ChangePropertyAction PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</ec:DataTrigger>
</interactivity:Interaction.Triggers>
</Button>
Note Answered from phone syntax may not be correct

When ViewModel is changed to a different instance a bound RadioButton is reset to False

I reproduced this issue in a pretty simple .NET 4.0 test project.
Using MVVM, I have a view with a user control on it. When the user clicks a button, I need to replace the user control's data context so it points to another instance of the same class. When that happens, the value of a bound radio button in the user control is reset, i.e. the ConvertBack method of my converter BooleanToRadioButtonConverter is called with a value of false.
My questions are: why does this happen and how do I work around it?
I want to preserve the state of the ViewModels and be able to switch back and forth.
Here is my test project's code:
The parent view:
<StackPanel>
<converters:RadioButtons DataContext="{Binding RadioButtonVM, Mode=OneWay}" />
<TextBlock Margin="20 0 0 0" VerticalAlignment="Bottom">
<Hyperlink Command="{Binding SwitchViewModel}" >
<Run Text="Switch ViewModel" />
</Hyperlink>
</TextBlock>
</StackPanel>
The parent ViewModel:
public class ParentViewModel : ViewModelBase
{
private ChildViewModel _vm1;
private ChildViewModel _vm2;
private ChildViewModel _currentChildVM;
public ParentViewModel()
{
_vm1 = new ChildViewModel();
_vm2 = new ChildViewModel();
_currentChildVM = _vm1;
}
public ChildViewModel RadioButtonVM
{
get { return _currentChildVM; }
}
public ICommand SwitchViewModel
{
get
{
return new RelayCommand(p =>
{
if (_currentChildVM == _vm1)
_currentChildVM = _vm2;
else
_currentChildVM = _vm1;
// Update the view
OnPropertyChanged("RadioButtonVM");
});
}
}
The child view:
<StackPanel Orientation="Horizontal">
<RadioButton Name="IsProtectedGroupFalse" GroupName="IsProtectedGroup" Margin="10 0 0 0"
IsChecked="{Binding IsProtected, Mode=TwoWay, Converter={StaticResource BooleanToRadioButtonConverter}, ConverterParameter=false}">
<Label Content="False" Target="{Binding ElementName=IsProtectedGroupFalse}" />
</RadioButton>
<RadioButton Name="IsProtectedGroupTrue" GroupName="IsProtectedGroup" Margin="10 0 0 0"
IsChecked="{Binding IsProtected, Mode=TwoWay, Converter={StaticResource BooleanToRadioButtonConverter}, ConverterParameter=true}">
<Label Content="True" Target="{Binding ElementName=IsProtectedGroupTrue}" />
</RadioButton>
</StackPanel>
The child ViewModel:
public class ChildViewModel : ViewModelBase
{
bool _isProtected;
public bool IsProtected
{
get
{
return _isProtected;
}
set
{
if (_isProtected == value)
return;
_isProtected = value;
OnPropertyChanged("IsProtected");
}
}
}
The converter:
class BooleanToRadioButtonConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Debug.WriteLine(" " + DateTime.Now.ToString("HH:mm:ss:fff") + " BooleanToRadioButtonConverter::Convert");
bool param = bool.Parse(parameter.ToString());
if (value == null)
return DependencyProperty.UnsetValue;
else
return !((bool)value ^ param);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
Debug.WriteLine(" " + DateTime.Now.ToString("HH:mm:ss:fff") + " BooleanToRadioButtonConverter::ConvertBack, value=" + value.ToString() + ", parameter=" + parameter.ToString());
bool param = bool.Parse(parameter.ToString());
if (value == null)
return DependencyProperty.UnsetValue;
else
return !((bool)value ^ param);
}
}
Update
Following Rachel's advice I replaced the RadioButtons with a CheckBox where the problem described above does not occur. I still do not know why it works the way it does, though.
This sounds like pretty standard behaviour to me. The reason it happens is that you are changing the ViewModel that the radio buttons are bound to, and your new ViewModel doesn't have the same values as the previous.
If you need to seed your new VM instance with data, make the change to the new instances property before you bind to the new instance to the datacontext.
Default value of a boolean is false. So, when you unbind the DataContext to that RadioButton it's IsChecked property returns to the default state of false.
I fixed the issue for the time being by adding an "active" property to the child ViewModel and manually setting that to true or false depending on whether the ViewModel is currently bound to the View or not. I do not consider that an ideal solution, though.

Conditional element in xaml depending on the binding content

Is it possible to display this TextBlock, only if the Address.Length > 0 ? I'd like to do this directly into the xaml, I know I could put all my controls programmatically
<TextBlock Text="{Binding Path=Address}" />
Basically, you're going to need to write an IValueConverter so that you can bind the Visibility property of your TextBox to either the Address field, or a new field that you create.
If you bind to the Address field, here's how the binding might look like::
<TextBlock Text="{Binding Path=Address}"
Visibility="{Binding Path=Address, Converter={StaticResource StringLengthVisibilityConverter}}" />
And then StringLengthVisiblityConverter could look something like this:
public class StringLengthVisiblityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.ToString().Length == 0)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Don't need to implement this
}
}
Then you'd just need to add your converter as a resource, using syntax like this (where src is mapped to the namespace where the converter is defined):
<src:StringLengthVisiblityConverter x:Key="StringLengthVisiblityConverter" />
I would do this with another boolean property called HasAddress which returns Address.Length > 0.
<!-- In some resources section -->
<BooleanToVisibilityConverter x:Key="Bool2VisibilityConverter" />
<TextBlock
Text="{Binding Address}"
Visibility="{Binding HasAddress, Converter={StaticResource Bool2VisibilityConverter}}"
/>
You should also remember to notify the property change for HasAddress in the setter of Address.
You can create a StringToVisibility converter.
It will return Visibility.Visible if bound string is not null or empty and Visibility.Collapsed if it is.
Use this converter while binding Address with Visibility property of your TextBlock.
Example:
<TextBlock Text="{Binding Path=Address}" Visibility="{Binding Address, Converter={StaticResource StringToVisibilityConverter}}" />

How to get the selected index of MVVM-bound radiobuttons?

I have an ItemsControl which is bound and set to an observablecollection in my viewmodel:
<ItemsControl ItemsSource="{Binding AwaySelection}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding AwayText}" ></RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now, how to find out which one is clicked? I would like to bind the IsChecked value of each Radiobutton to a single variable in the viewmodel that returns an index to the collection. This would make it very easy for me to directly reference the selected item. Any ideas?
This is how I solved this problem. I wrote an EnumToBool converter for this, like
public class EnumToBoolConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value,
Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (parameter.Equals(value))
return true;
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return parameter;
}
#endregion
}
And I've the following enumeration
public enum CompanyTypes
{
Type1Comp,
Type2Comp,
Type3Comp
}
Now, in my Xaml, I'm passing the types as the converter parameter.
<Window x:Class="WpfTestRadioButtons.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTestRadioButtons"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<local:EnumToBoolConverter x:Key="EBConverter"/>
</Window.Resources>
<Grid>
<StackPanel>
<RadioButton IsChecked="{Binding Path=Type,
Converter={StaticResource EBConverter},
ConverterParameter={x:Static local:CompanyTypes.Type1Comp}}" Content="Type1"/>
<RadioButton IsChecked="{Binding Path=Type,
Converter={StaticResource EBConverter},
ConverterParameter={x:Static local:CompanyTypes.Type2Comp}}" Content="Type2"/>
</StackPanel>
</Grid>
</Window>
Now, in your view model, you should have a property (in this case Type), which is of that Enum type.
Like,
public CompanyTypes Type
{
get
{
return _type;
}
set
{
_type = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Type"));
}
}
In this example, you might have noticed that Radiobuttons are static. In your case, as you are listing the radio buttons inside an Item control, you need to bind your ConverterParameter of your RadioButton as well, to the correct type.
In the end, I put the radio buttons into a listview, and bind the isselected property of the listview to the radiobutton one.
link Forum post describing this technique
When use MVVM with radiobutton control exits a problem on method onToggle(), but you can create a radiobutton for that.
public class DataBounRadioButton: RadioButton
{
protected override void OnChecked(System.Windows.RoutedEventArgs e) {
}
protected override void OnToggle()
{
this.IsChecked = true;
}
}
Then add reference to control and Binding a property, in my case IsActive.
<controls:DataBounRadioButton
IsChecked="{Binding IsActive}"/>

Categories

Resources