MvvmCross BackgroundColor binding - xaml

I'm trying to bind a text backgroundcolor from a label and change it from its ViewModel, but it isn't working for me.
View:
<StackLayout Grid.Row="4" Orientation="Horizontal">
<Label Text="Number of seats:" Grid.Row="4" FontAttributes="Bold" />
<Label Text="{Binding SeatsNumber}" Grid.Row="4" BackgroundColor="{Binding SeatsBackgroundColor}"/>
</StackLayout>
ViewModel:
public string SeatsBackgroundColor { get; set; }
And I setted to "Green" on the constructor, but when the application starts the color didn't change.
Do you know what I'm doing wrong?
EDIT:
Ok, I solved.
The issue was that the stacklayout the I showed was inside a ListView so I had to add a new property (to set the backgroundColor) inside the Model that I was using for fill the list.

Related

Get (and bind to) current displaywidth of element in Maui

In my xaml setup I have a Label, which should wrap its text. It is contained within a border, and I want the Label width to be the width of the border. I've tried to do this with the following binding on the label:
WidthRequest="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type Border}}, Path=Width}"
This has not worked though, and i suspect it hasn't because the Width that gets returned is NaN (I've written out the width of the border during runtime and it was NaN). But this is confusing to me, as I have a binding on the Border to the width of the Flexlayout which is working. Is there anything I'm missing and is there any better way to do this?
xaml:
<ScrollView Grid.Row="1">
<FlexLayout BindableLayout.ItemsSource="{Binding DisplayedUserActions}" JustifyContent="Start" Wrap="Wrap" Direction="Row">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Border FlexLayout.AlignSelf="Stretch" FlexLayout.Basis="{Binding Source={RelativeSource FindAncestor, AncestorType{x:Type ScrollView}}, Path=Width, Converter={StaticResource WidthToFlexlayoutBasisConverter}}">
<Grid RowDefinitions="*,*">
-- more content --
<Label Grid.Row="1" Text="{Binding Title}" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" VerticalOptions="Center" HorizontalOptions="Center" TextColor="Black" Margin="5" WidthRequest="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type Border}}, Path=Width}"/>
</Grid>
</Border>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</ScrollView>
You can use data binding to achieve the same width for Label and Border. Bind the Border and Label's WidthRequest properties to the TestWidth property in the same binding context. like this:
Xaml:
<StackLayout>
<StackLayout.BindingContext>
<local:MyViewModel/>
</StackLayout.BindingContext>
<Border BackgroundColor="Pink" WidthRequest="{Binding Testwidth}">
<Label x:Name="lable" Text="test1111111111111111" WidthRequest="{Binding Testwidth}"/>
</Border>
</StackLayout>
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public double testwidth;
public event PropertyChangedEventHandler PropertyChanged;
public double Testwidth
{
get { return testwidth; }
set
{
if (testwidth != value)
{
testwidth = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Testwidth"));
}
}
}
public MyViewModel() { testwidth= 100; }
}

Binding scope doesn't change inside collectionview/listview

I have an ObservableCollection of Stylist that I want to display in my view using a CollectionView
this is the code for collectionview
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:Appointments.ViewModels"
x:DataType="viewmodels:WallViewModel"
x:Class="Appointments.Views.WallPage">
<ContentPage.BindingContext>
<viewmodels:WallViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<FlexLayout
JustifyContent="SpaceBetween"
Margin="10, 20">
<Entry
WidthRequest="250"
Placeholder="search.."/>
<Button
Text="Filters"
Command="{Binding OpenFilterCommand}"
/>
</FlexLayout>
<CollectionView
x:Name="StylistList"
BackgroundColor="Transparent"
ItemsSource="{Binding Stylists}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
The Stylist model inherits from a User model that has a public property "Name"
public string Name { get; set; }
but if I run this code it will throw an error saying
"Binding: Propery "Name" not found on WallViewModel"
but if I change the that label to bind to
<Label Text="{Binding .}"/>
and while the app is running I change it back to
<Label Text="{Binding Name}"/>
it will work fine and display all the names inside the collection
Due to you set the code below into a User model, when you use x:DataType="viewmodels:WallViewModel" would thrown the error Property "Name" not found on "App8.WallViewModel"..
public string Name { get; set; }
If you change the use the x:DataType, you nned to use the class which include the Name property.
For more details about the x:DataType, please check the MS docs.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-
binding/compiled-bindings
As Jason said, remove the x:DataType="viewmodels:WallViewModel" would fix the this error.
After removing the x:DataType="viewmodels:WallViewModel, <Label Text="{Binding Name}"/> should work.
Just put the iterated type inside the DataTemplate:
<DataTemplate x:DataType="viewmodels:xxx">
...
</DataTemplate>

Scrolling when there are two CollectionViews in ContentPage

I have two CollectionViews in my ContentPage inside a StackLayout, one above the other. Each binds to a separate ItemsSource. Above each one I have a Label. At this point each one take up 50% of the screen and scrolls separately.
I would like everything to scroll as though it were one long list.
So I surrounded everything with a ScrollView. But then, depending on where you put your finger, the scroll may scroll the entire page (which is what I want) or just the current CollectionView.
It seems like there is no way to cancel the scroll capability of the CollectionView. Is that true? and if not, How should I set up my ContentPage ?
In the below example both CollectionViews have the same model and binding but in reality they will be different.
Here is the xaml:
<RefreshView
x:DataType="local:AllRestaurantsViewModel"
Command="{Binding LoadItemsCommand}"
IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<ScrollView>
<StackLayout>
<Label
FontSize="Large"
HorizontalOptions="Center"
Text="Suggested Restaurants" />
<CollectionView
x:Name="ItemsListView"
ItemsSource="{Binding SuggestedRestsComments}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" x:DataType="model:SuggestedRestsComment">
<Label
FontSize="16"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
Text="{Binding restaurantName}" />
<Label
FontSize="13"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemDetailTextStyle}"
Text="{Binding CityName}" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ItemsViewModel}}, Path=ItemTapped}"
CommandParameter="{Binding .}"
NumberOfTapsRequired="1" />
</StackLayout.GestureRecognizers>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Label
FontSize="Large"
HorizontalOptions="Center"
Text="Existing Restaurants" />
<CollectionView
x:Name="ItemsListView2"
ItemsSource="{Binding SuggestedRestsComments}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" x:DataType="model:SuggestedRestsComment">
<Label
FontSize="16"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
Text="{Binding restaurantName}" />
<Label
FontSize="13"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemDetailTextStyle}"
Text="{Binding CityName}" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ItemsViewModel}}, Path=ItemTapped}"
CommandParameter="{Binding .}"
NumberOfTapsRequired="1" />
</StackLayout.GestureRecognizers>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ScrollView>
</RefreshView>
You could have a try with Custom CollectionViewRenderer to achieve that in each platform.
For example, send a mesage in Forms:
void OnButtonClicked(object sender, EventArgs e)
{
MessagingCenter.Send<object>(this, "StopScrollinng");
}
Then in iOS CustomCollectionViewRenderer class stop scrolling:
public class CustomCollectionViewRenderer: CollectionViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<GroupableItemsView> e)
{
base.OnElementChanged(e);
MessagingCenter.Subscribe<object>(this, "StopScrollinng", (sender) =>
{
// Do something whenever the "StopScrollinng" message is received
if (Control != null)
{
NSArray s = Control.ValueForKey(new NSString("_subviewCache")) as NSMutableArray;
UICollectionView c = s.GetItem<UICollectionView>(0);
c.SetContentOffset(c.ContentOffset, true);
}
});
}
}
And in Android CustomCollectionViewRenderer class stop scrolling:
public class CustomCollectionViewRenderer: CollectionViewRenderer
{
public CustomCollectionViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ItemsView> elementChangedEvent)
{
base.OnElementChanged(elementChangedEvent);
MessagingCenter.Subscribe<object>(this, "StopScrollinng", (sender) =>
{
// Do something whenever the "StopScrollinng" message is received
this.DispatchTouchEvent(MotionEvent.Obtain(SystemClock.UptimeMillis(), SystemClock.UptimeMillis(), MotionEventActions.Cancel, 0, 0, 0));
});
}
}
How about this?
In your scrollview set InputTransparent="True" this allows the input to go through to the layer underneath.
<ScrollView InputTransparent="True">
Then leave some white space (background) on the right side of the collection views.
Now when someone swipes in the white space, the entire page scrolls. And when someone swipes inside the collection view, the collection view scrolls.
taken from https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/populate-data
the idea is to not use two separate collectionviews and merge them into one by choosing a datatemplate at runtime
xmlns:controls="clr-namespace:<your namespace>.Controls"
<ContentPage.Resources>
<DataTemplate x:Key="DataTemplate1">
...
</DataTemplate>
<DataTemplate x:Key="DataTemplate2">
...
</DataTemplate>
<controls:DataTemplateSelector1 x:Key="DataTemplateSelector1"
Template1="{StaticResource DataTemplate1}"
Template2="{StaticResource DataTemplate2}" />
</ContentPage.Resources>
namespace <your namespace>.Controls
{
public class DataTemplateSelector1: DataTemplateSelector
{
public DataTemplate Template1 { get; set; }
public DataTemplate Template2 { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
//here you return which template you want to use based on the properties of "item" . e.g you can do item is SomeClass ? Template1 : Template2
}
}
}
<ScrollView>
<CollectionView x:Name="collection" ItemTemplate="{StaticResource DataTemplateSelector1}"></CollectionView>
</ScrollView>

Binding behaviors from viewmodel in listview in xamarin.forms

I have a list view which I am using to display a form.
Here is my item source:
ObservableCollection<ContactBlockFieldViewModel> ContactBlockFIelds
Each ContactBlockFieldViewModel contains public EContactFieldTypeDTO ContactField { get; set; }
I want to bind the listview entry behavior property to specific behavior, depending on what type of contact field it is.
For example, for email type I would want my custom EmailValidationBehavior.
How can I accomplish this? I tried putting the behavior right in the viewmodel and then
<ListView ItemsSource="{Binding ContactBlockFields}" x:Name="ContactFieldsList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding FormDisplayName}" HorizontalTextAlignment="Start"></Label>
<Entry Text="{Binding Value}" BackgroundColor="{Binding BackgroundColor}">
<Entry.Behaviors>
<Binding Path="BehaviorInViewModel"></Binding>
</Entry.Behaviors>
</Entry>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
But it didn't work, I also tried to use a converter which would convert the enum to Behavior but also without success. I checked in debug and the constructor of the behavior gets called, but the object is not being bound.

Color Binding Not Working for Data Templates in Win8 App

I'm currently trying to add some sort of a Color Theme feature to a Win8 App I'm working at... I've though of making a binding from a vm, and everything works fine for static UI elements. But, I'm adding some notes (my model) into a DB, and they also appear on the screen into a GridView.
But in the declared DataTemplate for the GridView ItemTemplate, the Color Binding will not work at all...
My template looks like this :
<Grid Grid.Row="3" HorizontalAlignment="Left" Width="200" Height="200">
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="60"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="Lavender" Opacity="50"/>
<ScrollViewer Grid.Row="0">
<TextBlock Grid.Row="0" Text="{Binding Content}" Foreground="DodgerBlue" />
</ScrollViewer>
<Border Grid.Row="1" Background="DodgerBlue" Opacity="70"/>
<ScrollViewer Grid.Row="1">
<TextBlock Grid.Row="1" Text="{Binding Subject}" Foreground="LightBlue" />
</ScrollViewer>
<Border Grid.Row="2" Background="DodgerBlue" Opacity="70"/>
<TextBlock Grid.Row="2" Text="{Binding Importance}" Foreground="Black" FontSize="{StaticResource ComboBoxArrowThemeFontSize}" />
</Grid>
What I tried was simply instead of Foreground="DodgerBlue" to Foreground="{Binding ColorTheme}" but it had no effect, the SolidColorBrush was not acquired from vm....
Is there any workaround for this?
Many thanks in advance.
Altough I didn't really find out the actual explanation on why the below works (and I hope someone more experience, maybe could explain me), this post, seemed to have worked for me.
Not very sure, but was a matter of DataContext, so instead of trying a binding like this : Foreground={Binding ColorTheme}, I changed to this : Foreground={Binding DataContext.ColorTheme, ElementName=MyGridViewName}".
Color binding (or better: brush binding) should work just fine - in GridView.ItemTemplate scenario and elsewhere.
You haven't given enough info to pinpoint why it doesn't work in your case, but here is a small sample I've just tried out:
GridView to put in your page:
<GridView Width="600" Height="200" ItemsSource="{Binding GridItems}"
x:Name="GridViewName">
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Width="50" Height="50"
Foreground="{Binding Color}" Text="{Binding Text}" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
View model class bound as DataContext for your page:
public class ViewModel
{
public ViewModel()
{
GridItems = new List<GridItem>
{
new GridItem {Text = "First", Color = new SolidColorBrush(Colors.White)},
new GridItem {Text = "Second", Color = new SolidColorBrush(Colors.Yellow)},
new GridItem {Text = "Third", Color = new SolidColorBrush(Colors.Green)},
new GridItem {Text = "Fourth", Color = new SolidColorBrush(Colors.Red)},
};
}
}
GridItem class:
public class GridItem
{
public string Text { get; set; }
public Brush Color { get; set; }
}
The result:
EDIT:
I need to point out that inside data template DataContext is set to the current item of the bound collection (GridItems in my case) not to the page DataContext (ViewModel in my case).
This means that by setting {Binding Color} to a control property you're binding to GridItem.Color not to ViewModel.Color. To make the latter work you first need to access the parent DataContext using the ElementName syntax as you already suggested in your own answer: {Binding DataContext.ColorTheme, ElementName=GridViewName} - this binds to a named element on the page and allows you to access its properties, DataContext being ViewModel in this case.