Bind value for "Maximum' property not working on Slider Xamarin - xamarin

I am trying to use a ViewModel property on Slider Maximum value, but when I use Binding the slider always show the current value as 0.
With fixed values, it works:
<Slider Minimum="0" Maximum="16" Value="{Binding SliderStep, Mode=OneWay}" HorizontalOptions="CenterAndExpand" />
But the Maximum value is variable, so I need to Bind it from ViewModel, when I try do it something goes wrong:
<Slider Minimum="0" Maximum="{Binding SliderMax}" Value="{Binding SliderStep, Mode=OneWay}" HorizontalOptions="CenterAndExpand" />
ViewModel Piece Code:
double sliderStep;
public double SliderStep
{
get
{
return sliderStep;
}
set { sliderStep = value;
OnPropertyChanged("SliderStep");
}
}
double sliderMax;
public double SliderMax
{
get
{
return sliderMax;
}
set {
sliderMax = value;
OnPropertyChanged();
}
}
public SectionGraphicViewModel(INavigation navigation)
{
this.Navigation = navigation;
sectionRep = new SectionRepository();
this.Sections = sectionRep.GetSections().ToList();
Items = new List<SectionGraphicModel>();
foreach (var item in this.Sections)
{
var SectionView = new SectionModel(item);
Items.Add(new SectionGraphicModel(){ Source = SectionView.CachedImageUsed.Source, Desc = SectionView.Desc, Id = SectionView.Id });
}
SliderMax = items.Count();
if (sectionId.HasValue)
{
SliderStep = items.FindIndex(x => x.Id == sectionId);
}
else
{
SliderStep = 0;
}
}
The Maximum value on this example is 16, I tested with different values and the result is the same.
I tested only on Android simulator. I don't know if it happens on iOS.
PS: Just to avoid misunderstanding, I need to make the Maximum property Bind a value from VM, and OneWay, the control will be disabled from UI, just showing the value from VM.

You should set properties in an order that ensures that Maximum is always greater than Minimum, Maximum should be set before Minimum in xaml:
<Slider BackgroundColor="Aquamarine" Maximum="{Binding SliderMax}" Minimum="0" Value="{Binding SliderStep}" IsEnabled="False" VerticalOptions="EndAndExpand" ></Slider>
If you want to disable the UI, set:
IsEnabled="False"
Refer: slider#precautions

I did not read the question correctly. The issue is just binding mode
Value="{Binding SliderStep, Mode=TwoWay}
This should do it.
PS: Whenever you upload a sample repo remove all your bin/obj files from all shared and target projects. Your project is less than 2 MB but the zip I downloaded because of bin/obj was 50+ MB.

Related

Binding FontSize Not working Xamarin Forms?

XAML:
<Button Text="Submit" FontSize="{Binding BtnFontSize,Mode=Twoway}"/>
ViewModel:
string _btnFontSize;
public string BtnFontSize
{
get { return _saveBtnFontSize; }
set
{
_saveBtnFontSize = value;
OnPropertyChanged();
}
}
while setting first time its works = > BtnFontSize="Large"
after it not working => BtnFontSize="Small"
Do the following changes and it should work:
Remove two-way binding as it is not needed
<Button Text="Submit" FontSize="{Binding BtnFontSize}"/>
In the OnPropertyChanged method pass the property name
private double _btnFontSize;
public double BtnFontSize
{
get { return _saveBtnFontSize; }
set
{
_saveBtnFontSize = value;
OnPropertyChanged(nameof(BtnFontSize));
}
}
And to get the default xamarin forms label font sizes you will use the NamedSize enum:
which should be something like below for eg to set the size to Medium you will do the following
BtnFontSize= Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
Where typeof(Label) signifies the control you are using so when you need to set button font size you will pass button here

How can I bind the value of a field to a label in Xamarin?

Here is what I tried. Note that I removed parts of the XAML to make the question shorter but I tested it out by setting detailx.text in my cs and when I do it that way it displays XXXX:
<Frame x:Class="Japanese.PhrasesFrame">
<StackLayout x:Name="phrasesFrameStackLayout" >
<Grid x:Name="phraseGrid">
<Grid x:Name="wordGrid" >
<Grid x:Name="detailGrid">
<Label x:Name="detail1" Text="{Binding English}" XAlign="Center" />
<Label x:Name="detailx" XAlign="Center" />
In my CS I have
public PhrasesFrame()
{
InitializeComponent();
correctButton.Clicked += correctButtonClicked;
resetButton.Clicked += resetButtonClicked;
SetBindings();
wordGrid.BindingContext = AS.phrase;
}
In a method I have this:
public partial class PhrasesFrame : Frame
{
Random rand = new Random();
public PhrasesFrame()
{
InitializeComponent();
wordGrid.BindingContext = AS.phrase;
AS.phrase = new PSCViewModel() { English = "abcd" };
this.detailx.Text = "XXXX";
}
My problem is that when I used Binding English then the label never shows the text abcd but I do see the XXXX
Can someone tell me if I am missing something obvious. I tried everything but no success.
Try changing the order, like this:
AS.phrase = new PSCViewModel() { English = "abcd" };
wordGrid.BindingContext = AS.phrase;
Whenever you set the BindingContext at that moment it is applied, as is. If you make changes afterwards, you can, but you need to implement the INotifyPropertyChanged event to fire off a signal to the UI indicating which property has changed. Only then will it be update in the UI.

Binding Canvas X,Y when used as ItemsControl ItemsPanel

I have a canvas and I would love to bind some points there on specified XY location. However, I can't achieve this with ItemsControl. I found some solutions but I guess they are not for Windows Phone.
The XAML I'm using is:
<ItemsControl ItemsSource="{Binding Path=Nodes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=XPos}" />
<Setter Property="Canvas.Top" Value="{Binding Path=YPos}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
And I get: "The member "ItemContainerStyle" is not recognized or is not accessible"
How to do that kind of binding in other way? I have name of object, and X/Y values and I want to bind is as a some kind of pushpin
I encountered the same issue but solved it using a TriggerAction instead. You can use the System.Windows.Interactivity if you have the Blend SDK. The dll is located in
c:\Program Files\Microsoft SDKs\Expression\Blend\Silverlight\v4.0\Libraries\
System.Windows.Interactivity.dll`
Then, by using your previous xaml code I can set the datatemplate as such:
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Stroke="Red" Width="2" Height="2">
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<tr:SetCanvasPropertiesAction Left="{Binding X}" Top="{Binding Y}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
Note the ia:Interaction prefix, from the interactivity dll mentioned before. You load it with
xmlns:ia="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
in the top of your xaml file.
The tr prefix is for including my own class, which looks like this:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
using System.Windows.Media;
namespace Presentation.Triggers {
public class SetCanvasPropertiesAction : TriggerAction<DependencyObject> {
public static readonly DependencyProperty LeftProperty =
DependencyProperty.Register("Left", typeof(double), typeof(SetCanvasPropertiesAction), new PropertyMetadata(default(double)));
public static readonly DependencyProperty TopProperty =
DependencyProperty.Register("Top", typeof(double), typeof(SetCanvasPropertiesAction), new PropertyMetadata(default(double)));
public double Top {
get { return (double)GetValue(TopProperty); }
set { SetValue(TopProperty, value); }
}
public double Left {
get { return (double)GetValue(LeftProperty); }
set { SetValue(LeftProperty, value); }
}
protected override void Invoke(object parameter) {
UIElement presenter = (UIElement)VisualTreeHelper.GetParent(AssociatedObject);
Canvas.SetLeft(presenter, Left);
Canvas.SetTop(presenter, Top);
}
}
}
Two things to note with the Invoke method. The first is AssociatedObject, that gets resolved to the Ellipse since the trigger is nested under it in the xaml. The second thing is the VisualTreeHelper, which gets the parent to the ellipse. This is the ContentPresenter on which we want to set the attached properties of the canvas.
It might look like it's more complicated, but as with everything else in mvvm, you can reuse it in xaml and you don't have to copy and paste code-behind code everywhere.
Finally I did it in C# only using foreach.
foreach (var InsidePoints in _inside)
{
double left = InsidePoints.xaxis * Layer_3.Width;
double top = InsidePoints.yaxis * Layer_3.Height;
Image pinImage = new Image();
pinImage.Name = InsidePoints.id.ToString();
pinImage.Source = (ImageSource)new ImageSourceConverter().ConvertFromString("icons/inside_pin.png");
pinImage.Tap += new EventHandler<System.Windows.Input.GestureEventArgs>(pinImage_Tap);
//pinImage.Hold += new EventHandler<System.Windows.Input.GestureEventArgs>(pinImage_Hold);
Layer_3.Children.Add(pinImage);
Canvas.SetLeft(pinImage, left);
Canvas.SetTop(pinImage, top);
}

How are databound views rendered?

When a Windows Phone 7 application opens a view, a certain order of business is followed in order to create. As far as constructors and events go, I have found this order to be true:
Constructor
OnNavigatedTo
OnLoaded
However, I am in a position where I need to databind a List to a ListBox after the basic view (background, other elements etc) has loaded. So I need to know when and how to know that the view is loaded before I get on with the data binding.
I have tried to do this on the OnLoaded-event, but it seems like if I do the data binding here - and right after it traverse those elements - they don't seem to exist yet (the VisualTreeHelper-class can't seem to find the nodes). So as you see, I am stuck.
Any help would be greatly appreciated.
Edit: As requested, here is some more information about what's going on.
My List is populated by some custom (not too complicated) objects, including an asynchronously loaded image (courtesy of delay.LowProfileImageLoader) and a rectangle.
The XAML:
<ListBox x:Name="ChannelsListBox" ItemsSource="{Binding AllChannels}">
//...
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="ChannelTile" Margin="6,6,6,6" Tap="ChannelTile_Tap" Opacity="0.4">
<!-- context menu goes here -->
<Rectangle Width="136" Height="136" Fill="{StaticResource LightGrayColor}" />
<Image Width="136" Height="136" delay:LowProfileImageLoader.UriSource="{Binding ImageUri}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The code-behind:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
UpdateApplicationBar();
pickChannelsViewModel = new PickChannelsViewModel();
DataContext = pickChannelsViewModel;
if (hasUpdatedTiles)
{
pickChannelsViewModel.IsLoading = false; // Set by UpdateTiles()
}
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
// This is where I would data bind the list (instead of in XAML)
UpdateTiles(); // Traverses the list and changes opacity of "selected" items.
}
protected void UpdateTiles()
{
foreach (var item in ChannelsListBox.Items)
{
if (pickChannelsViewModel.SelectedChannels.Contains(item as Channel))
{
var index = ChannelsListBox.Items.IndexOf(item);
// This returns null when databinding in codebehind,
// but not in XAML
ListBoxItem currentItem = ChannelsListBox.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
if (currentItem != null && VisualTreeHelper.GetChildrenCount(currentItem) == 1)
{
var OuterWrapper = VisualTreeHelper.GetChild(currentItem, 0);
var MiddleWrapper = VisualTreeHelper.GetChild(OuterWrapper, 0);
var InnerWrapper = VisualTreeHelper.GetChild(MiddleWrapper, 0);
Grid currentItemGrid = VisualTreeHelper.GetChild(InnerWrapper, 0) as Grid;
currentItemGrid.Opacity = 1.0;
}
}
}
pickChannelsViewModel.IsLoading = false;
hasUpdatedTiles = true;
}
The items themselves are in-memory (fetched from REST at an earlier stage in the application), so should be available instantaneously.
The issue I am trying to resolve is a fairly long load time on this particularly view (there is about 140 of these items being created, then filtered through and changing the opacity).
I believe you are doing something like:
myListBox.ItemSource=myList;
Once you set the ItemSource of a ListBox the changes in your List should be reflected in the ListBox at all times. If the ListBox is empty the reason must be that the List is not being populated properly or invalid Bindings in the ItemTemplate. You should debug and find out if your List has any items by inserting a breakpoint in the Loaded() method. Also, you've not mentioned what items does your List contains or, where is it being populated in the application? Incomplete information doesn't help anyone.

Using ListPicker and DataBinding

Ok. I give up.
I want to use ListPicker control in one of my Windows Phone apps. I am getting an Exception SelectedItem must always be set to a valid value.
This is my XAML piece of ListPicker:
<toolkit:ListPicker x:Name="CategoryPicker"
FullModeItemTemplate="{StaticResource CategoryPickerFullModeItemTemplate}"
Margin="12,0,0,0"
ItemsSource="{Binding CategoryList}"
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"
ExpansionMode="ExpansionAllowed"
FullModeHeader="Pick Categories"
CacheMode="BitmapCache"
Width="420"
HorizontalAlignment="Left" />
CategoryList is an ObservableCollection<Category> in my ViewModel.
SelectedCategory is a property in my ViewModel of type Category.
This is how I am declaring both CategoryList and SelectedCategory:
private Category _selectedCategory;// = new Category();
private ObservableCollection<Category> _categoryList = new ObservableCollection<Category>();
public ObservableCollection<Category> CategoryList
{
get
{
return _categoryList;
}
set
{
_categoryList = value;
RaisePropertyChanged("CategoryList");
}
}
public Category SelectedCategory
{
get
{
return _selectedCategory;
}
set
{
if (_selectedCategory == value)
{
return;
}
_selectedCategory = value;
RaisePropertyChanged("SelectedCategory");
}
}
Appreciate your help!!! Maybe I have not understood the usage of ListPicker very well.
I'd expect the object returned by SelectedCategory to be one of the objects from the CategoryList collection. In your example you are instanciating it within the get, so this is definitely not the case.
If CategoryList contains some values, then perhaps initialize _selectedCategory to null, and then in the get
if(_selectedCategory == null) {
_selectedCategory = CategoryList.First();
}
Take a look at my answer to this question:
Silverlight ComboBox binding with value converter
The short answer is that the selected item must be an item that is contained within the collection. Your getter is setting the selected item to a new object. This new object is not contained within the collection

Resources