RadioGroup SelectValue Binding - xaml

Good morning
I have a problem with binding initial state for each radio button.
<StackLayout Orientation="Horizontal"
RadioButtonGroup.GroupName="{Binding Source}"
RadioButtonGroup.SelectedValue="{Binding Frequency}">
<RadioButton FontSize="10" Content="Never" Value="0"/>
<RadioButton FontSize="10" Content="Rare" Value="25"/>
<RadioButton FontSize="10" Content="Often" Value="75"/>
<RadioButton FontSize="10" Content="Always" Value="100"/>
</StackLayout>
When the view is loaded there is no any RadioButton selected. The source binding Frequency is of type double.
public double Frequency
{
get => GetPropertyValue<double>();
set => SetPropertyValue(value);
}
I was thinking it is related with comparassion of types object and double. I have created converter as below:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!int.TryParse(value.ToString(), out var intValue))
return value;
var result = FrequencyValues.GetValue(intValue).ToString();
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!int.TryParse(value.ToString(), out var intValue))
return value;
var result = FrequencyValues.GetValue(intValue);
return result;
}
The solution with converter is also not working. Is that related with comprassion of types or I am missing some knowledge?
Thanks!

try
<StackLayout
RadioButtonGroup.GroupName="Source"
RadioButtonGroup.SelectedValue="{Binding Frequency}"
>
<RadioButton Content="a" Value="1"
IsChecked="{Binding Frequency, Converter={StaticResource RadioIsCheckedConverter}, ConverterParameter={x:Reference page}}" />
<RadioButton Content="b" Value="2"
IsChecked="{Binding Frequency, Converter={StaticResource RadioIsCheckedConverter}, ConverterParameter={x:Reference page}}" />
</StackLayout>
converter
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
var radioButton = parameter as RadioButton;
if (radioButton != null && radioButton.Value.Equals(value))
{
return true;
}
}
return false;
}

Change the type of Frequency from Double to Object;
The same Xaml as yours.
Code behind:
public partial class Page1 : ContentPage
{
RadioButtonModel _viewModel;
public Page1()
{
InitializeComponent();
_viewModel = new RadioButtonModel() { Source = "Group1", Frequency="100" };
this.BindingContext = _viewModel;
}
}
public class RadioButtonModel : INotifyPropertyChanged
{
private object _frequency;
public object Frequency
{
get => _frequency;
set
{
_frequency = value;
GetPropertyValue(nameof(Frequency));
}
}
private string _source;
public string Source
{
get => _source;
set
{
_source = value;
GetPropertyValue(nameof(Source));
}
}
public event PropertyChangedEventHandler PropertyChanged;
void GetPropertyValue(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Related

Assignment to ColumnDefinitions with binding to xamarin forms

I need the column to be dynamic and the values ​​to change when I click the button.
I made a method that, when activated, will pass the value to a variable that will be used for association through binding.
private void clickedColumnDefinition()
{
ColumnDefinition1 = 420;
}
The property this method have a declared GrindLenght:
private GridLength _columnDefinition1;
public GridLength ColumnDefinition1 {
get { return _columnDefinition1; }
set { SetProperty(ref _columnDefinition1, value); }
}
In xaml define the column like this:
<ColumnDefinition Width="{Binding ColumnDefinition1, Converter={StaticResource numberToGridLengthConverter}}"/>
The converter I'm using is this one, but nothing happens when I press the button.
public class NumberToGridLengthConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var numberValue = (double)value;
return new GridLength(numberValue, GridUnitType.Star);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var gridLength = (GridLength)value;
return gridLength.Value;
}
}
what can be wrong?
If you want to set the RowDefinition's Height to a Dynamic value. You can do as follows:
1.created a view model RowHeightClass
public class RowHeightClass: INotifyPropertyChanged
{
bool swith;
//public GridLength rowFirstHeight { set; get; }
GridLength _rowFirstHeight;
public GridLength RowFirstHeight
{
get => _rowFirstHeight;
set => SetProperty(ref _rowFirstHeight, value);
}
//public GridLength rowSecondHeight { set; get; }
GridLength _rowSecondHeight;
public GridLength RowSecondHeight
{
get => _rowSecondHeight;
set => SetProperty(ref _rowSecondHeight, value);
}
public RowHeightClass()
{
//initial RowFirstHeight and RowSecondHeight
RowFirstHeight = new GridLength(15);
RowSecondHeight = new GridLength(15);
}
public ICommand ResetHeightCommand => new Command(resetHeight);
private void resetHeight()
{ //reset RowFirstHeight and RowSecondHeight
if (!swith)
{
RowFirstHeight = new GridLength(50);
RowSecondHeight = new GridLength(50);
}
else {
RowFirstHeight = new GridLength(15);
RowSecondHeight = new GridLength(15);
}
swith = !swith;
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
2.bind the properties in MainPage.xaml as follows:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DynamicHeightApp.MainPage">
<StackLayout>
<Grid BackgroundColor="Orange">
<Grid.RowDefinitions>
<RowDefinition Height="{Binding RowFirstHeight}" />
<RowDefinition Height="{Binding RowSecondHeight}"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Text="Testing Color Change on this one" TextColor="Red"/>
<Label Grid.Row="1" Text="Testing Height" />
<Label Grid.Row="2" Text="Testing Height Change on this one through binding"/>
</Grid>
<Button Text="reset" Command="{Binding ResetHeightCommand}"/>
</StackLayout>
</ContentPage>
3.MainPage.xaml.cs
public partial class MainPage : ContentPage
{
RowHeightClass rowHeightClass;
public MainPage()
{
InitializeComponent();
rowHeightClass = new RowHeightClass();
BindingContext = rowHeightClass;
}
}
Note:
I added two properties RowFirstHeight and RowSecondHeight and implemented interface INotifyPropertyChanged for this view model, if we change the value of above properties, the UI will update automatically.

PropertyChangedCallback is not getting fired during callback when a value in ViewModel is changed

I have a view that uses the SearchBox user control, The SearchBox has two radio buttons to select the search modes - Instant and delayed. I have binded the searchmodes to SearchMode property, and also I have created a custom dependency property for the Search Mode.
View
<controls:SearchBox Grid.Row="0"
HorizontalAlignment="Right"
Margin="2" Width="200"
SearchMode="{Binding DataContext.SearchMode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged }" />
ViewModel.cs
private Mode mSearchMode;
public Mode SearchMode
{
get
{
return mSearchMode;
}
set
{
mSearchMode = value;
NotifyOfPropertyChange();
}
}
// Called when application is restarted.
private void ActivateLastSelectedSearchMode(Mode lastselectedMode)
{
// Sets the last selected mode to the search mode
SearchMode = lastselectedMode;
}
public enum Mode
{
Instant,
Delayed,
}
SearchBox.xaml
<UserControl x:Class = "abc.SearchBox"
DataContext="{Binding RelativeSource={RelativeSource Self}}" >
<UserControl.Resources>
<converters:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<RadioButton Content="{lex:Loc SearchBox:SearchModelInstatOption}"
IsChecked="{Binding Path=SearchMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Mode.Instant}}" />
<RadioButton Content="{lex:Loc SearchBox:SearchModeDelayedOption}"
IsChecked="{Binding Path=SearchMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static local:Mode.Delayed}}" />
</StackPanel>
</UserControl>
SearchBox.xaml.cs
public partial class SearchBox : UserControl
{
public static DependencyProperty SearchModeProperty =
DependencyProperty.Register(
"SearchMode",
typeof(Mode),
typeof(SearchBox),
new FrameworkPropertyMetadata(default(Mode), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsSearchModeChanged));
static void OnIsSearchModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var searchBox = obj as SearchBox;
searchBox.SearchMode = (Mode)e.NewValue;
}
public Mode SearchMode
{
get { return (Mode)GetValue(SearchModeProperty); }
set { SetValue(SearchModeProperty, value); }
}
}
I want the OnIsSearchModeChanged() to be fired each time when SearchMode is set during call back i e, ActivateLastSelectedSearchMode() is invoked in ViewModel.cs. I am absolutely clueless..where I am missing, I am unable to achieve success.
//snip
private Mode mSearchMode;
public Mode SearchMode
{
get
{
return mSearchMode;
}
set
{
mSearchMode = value;
NotifyOfPropertyChange(()=>SearchMode); //Change
}
}
does the reflected change make any difference? Other option would be to create a custom convention for your user control
You should create an Event in you View Model and subscribe to it from your code behind.
In your View Model :
public event SearchModeAction SearchModeChanged;
public delegate void SearchModeAction(object sender, EventArgs e);
public void SearchModeHasChanged()
{
SearchModeAction Handler = SearchModeChanged;
if (Handler != null)
{
Handler(this, null);
}
}
private void ActivateLastSelectedSearchMode(Mode lastselectedMode)
{
// Sets the last selected mode to the search mode
SearchMode = lastselectedMode;
SearchModeHasChanged()
}
In your Code Behind :
private void Window_Loaded(object sender, RoutedEventArgs e)
{
((YourViewModelClass)DataContext).SearchModeChanged += OnIsSearchModeChanged;
}
private void OnIsSearchModeChanged(object sender, EventArgs e)
{
var searchBox = obj as SearchBox;
searchBox.SearchMode = (Mode)e.NewValue;
}
This way each time you arrive in your ActivateLastSelectedSearchMode method in your View Model, you will call your OnIsSearchModeChanged method in your View.
Ahh..the reason was the EnumToBooleanConverter.
Though the value of 'parameter' and 'value' was same, There was a difference between their object types as both were referencing to different namespaces. So I created a public enum called 'Mode' and ensured that the 'Instant' and 'Delayed' reference to the same namespace.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return false;
}
return value.Equals(parameter); // This always returned false despite the values being the same
}

Can I declare more than one converter? WP8

I am using BooleanToVisibilityConvertor for manipulating visibility of TextBlock in a ListBox DataTemplate.
Here is my XAML code:
<phone:PhoneApplicationPage.Resources>
<Converters:BooleanToVisibilityConvertor x:Key="booleanToVisibilityConvertor"/>
</phone:PhoneApplicationPage.Resources>
<TextBlock Grid.Row="2" HorizontalAlignment="Right" Padding="0,0,7,0" Visibility="{Binding AverageConsumption, Converter={StaticResource booleanToVisibilityConvertor}}">
<Run Text="{Binding AverageConsumption}"/>
<Run Text="l./100 km."/>
</TextBlock>
And the code behind:
public class BooleanToVisibilityConvertor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
{
if (value != null)
{
if (!string.IsNullOrEmpty(value.ToString()))
{
return Visibility.Visible;
}
}
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This is working correct, but i need one more (reversed) convertor for another TextBlock placed on the same position (Grid.Row="2" HorizontalAlignment="Right")
with static Text="Partial Refueling" so when {Binding AverageConsumption} is not null or empty first TextBlock will be vissible and second will be collapsed and vice versa. Something like this:
class BooleanToVisibilityConvertorReversed : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
{
if (value != null)
{
if (!string.IsNullOrEmpty(value.ToString()))
{
return Visibility.Collapsed;
}
}
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I have tried to declare another convertor in XAML:
<phone:PhoneApplicationPage.Resources>
<Converters:BooleanToVisibilityConvertor x:Key="booleanToVisibilityConvertor"/>
<Converters:BooleanToVisibilityConvertorReversed x:Key="booleanToVisibilityConvertorReversed"/>
</phone:PhoneApplicationPage.Resources>
But got an exception on InitializeComponent():
'A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in System.Windows.ni.dll'
How can i solve this?
Seems like I've found what is your problem. Your BooleanToVisibilityConvertorReversed class is not public. Make it public and your issues should be gone.

XAML ComboBox default SelectedIndex is not shown

I have a ComboBox with some values, and I want to have two things working at once.
Here is my ComboBox and I want to show the 10 as default value and also to bind it to a double? Distance property.
<ComboBox Grid.Row="5" Grid.Column="1"
SelectedIndex="1"
SelectedValue="{Binding Distance, Mode=TwoWay, Converter={StaticResource StringToDoubleConverter}}">
<ComboBoxItem>1</ComboBoxItem>
<ComboBoxItem IsSelected="True">10</ComboBoxItem>
<ComboBoxItem>100</ComboBoxItem>
<ComboBoxItem>1000</ComboBoxItem>
</ComboBox>
And here is the converter:
public class StringToDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
ComboBoxItem item = value as ComboBoxItem;
if (item != null)
{
double d;
if (double.TryParse(item.Content.ToString(), out d))
return d;
}
return null;
}
}
The problem is that in this code, The selected item 10 is not show at the start of the application.
If I will remove the line with the converter, then it will show the selected item 10, but then, I can't bind it to the double? Distance property. I dont want to write a code behind for it, such as: Convert.ToDouble(combobox1.SelectedValue)...
What can I do to make both things work?
You need to populate combo box items from ViewModel. Moreover you should not use SelectedValue property, instead of it you should use SelectedItem. See the below given code.
XAML
<ComboBox x:Name="cmb" ItemsSource="{Binding DistanceCollection}"
SelectedItem="{Binding Distance, Converter={StaticResource StringToDoubleConverter}, Mode=TwoWay}"/>
ViewModel
public class viewModel : INotifyPropertyChanged
{
public viewModel()
{
DistanceCollection = new ObservableCollection<string>
{
"1",
"10",
"100",
"1000"
};
Distance = double.Parse(DistanceCollection[1].ToString());
}
public ObservableCollection<string> DistanceCollection { get; set; }
private double _Distance;
public double Distance
{
get
{
return _Distance;
}
set
{
_Distance = value;
OnPropertyChanged("Distance");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Converter
public class StringToDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
string item = value as string;
if (!string.IsNullOrWhiteSpace(item))
{
double d;
if (double.TryParse(item, out d))
return d;
}
return null;
}
}

How to change the background color of a Silverlight DataGridRow?

I have a Silverlight DataGrid bound to a collection of MyObjects. MyObject has a boolean field called IsHighlighted.
I would like to change the row's background color when this value is true. And to have it changed back if it becomes false.
I already tried by using the Loading_Rowevent (as explained here), but it didn't work for me, as this event is only called once, and my objetcs all have the boolean value set to false at this time (it only becomes truc when another component is selectes; this works, I checked the values).
Anybody has a clue ? Thanks in advance !
Update: I made a test application to illustrate, it reproduces my problem.
<navigation:Page x:Class="AViews.Tests"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignWidth="640" d:DesignHeight="480"
Title="Tests Page">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<sdk:DataGrid Grid.Row="0" ItemsSource="{Binding AllItems, Mode=TwoWay}" AutoGenerateColumns="False" LoadingRow="DataGrid_LoadingRow">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Binding="{Binding Value1}" Header="Value1" />
<sdk:DataGridTextColumn Binding="{Binding Value2}" Header="Value2"/>
<sdk:DataGridCheckBoxColumn Binding="{Binding IsHighlighted}" Header="Is Highlighted" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
<Button Content="Change !" Grid.Row="1" HorizontalAlignment="Left" Click="Button_Click" />
</Grid>
</navigation:Page>
public partial class Tests : Page, INotifyPropertyChanged
{
private SampleConverter bgConverter = new SampleConverter();
Random r = new Random();
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<Sample> allItemsField = new ObservableCollection<Sample>();
public ObservableCollection<Sample> AllItems
{
get
{
return this.allItemsField;
}
set
{
if (this.allItemsField != value)
{
this.allItemsField = value;
this.OnPropertyChanged("AllItems");
}
}
}
public Tests()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var tmp = Enumerable.Range(0, 100).Select(f => new Sample(f)).ToList();
foreach (var item in tmp)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
var coll = new ObservableCollection<Sample>(tmp);
this.AllItems = coll;
}
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.OnPropertyChanged("AllItems");
}
private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
Binding b = new Binding("IsHighlighted")
{
Mode = BindingMode.OneWay,
Converter = this.bgConverter,
ValidatesOnExceptions = true
};
e.Row.SetBinding(DataGridRow.BackgroundProperty, b);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (var item in this.AllItems)
{
item.IsHighlighted = r.Next(1000) % 2 == 0;
}
}
}
public class Sample: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private string value1Field = string.Empty;
public string Value1
{
get
{
return this.value1Field;
}
set
{
if (this.value1Field != value)
{
this.value1Field = value;
this.OnPropertyChanged("Value1");
}
}
}
private string value2Field = string.Empty;
public string Value2
{
get
{
return this.value2Field;
}
set
{
if (this.value2Field != value)
{
this.value2Field = value;
this.OnPropertyChanged("Value2");
}
}
}
private bool isHighlightedField = false;
public bool IsHighlighted
{
get
{
return this.isHighlightedField;
}
set
{
if (this.isHighlightedField != value)
{
this.isHighlightedField = value;
this.OnPropertyChanged("IsHighlighted");
}
}
}
public Sample(int index)
{
this.Value1 = string.Format("Value1 #{0}", index);
this.Value2 = string.Format("Value2 #{0}", index);
}
}
public class SampleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool val = (bool)value;
SolidColorBrush ret = val ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Green);
return ret;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And the result can be seen on those pictures:
When I first arrive on the page.
I click the button, which sets some (random) values to true. As you can see, the binding is updated, not the UI.
I use the scrollbar, go to the end, and come back, and Oh! wonderful! All the rows are correctly colored :-(