I'm using a bindable layout to display a list of items. The items are bound to an ObservableRangeCollection of key-value pairs
public ObservableRangeCollection<KeyValuePair<string, string>> Items { get; set; }
This is the layout that renders the Items -- each one of the rows are separated by a line using BoxView:
<StackLayout BackgroundColor="Transparent" BindableLayout.ItemsSource="{Binding Items}" Padding="20">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<Label Grid.Column="0"
Grid.Row="0"
Text="{Binding Key}"/>
<Label Grid.Column="1"
Grid.Row="0"
HorizontalOptions="End"
HorizontalTextAlignment="End"
Text="{Binding Value}"/>
<BoxView Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="2"
HeightRequest="1" />
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
The issue is there is an extra line at the end of the list which I wish to hide after rendering the last element in the observable collection.
I would need to figure out if the last item is being rendered on the BindableLayout and set the IsVisible property of the BoxView to false but I'm not sure how to do this using XAML.
One approach is to define a class with a boolean property, that you set on each item to true or false. This controls whether that BoxView appears.
MyItem.cs:
public class MyItem
{
public string Key { get; set; }
public string Value { get; set; }
/// <summary>
/// Default to "true". Most items will show the line.
/// </summary>
public bool ShowLineUnder { get; set; } = true;
MyModelView.cs:
public class MyModelView
{
public ObservableCollection<MyItem> Items { get; set; }
public MyModelView()
{
Items.Add(new MyItem(...));
Items.Add(new MyItem(...));
Items.Add(new MyItem(...));
// Hide line on last item.
Items[Items.Count - 1].ShowLineUnder = false;
}
}
in XAML of ItemTemplate:
<BoxView ... IsVisible="{Binding ShowLineUnder}" />
Adapt as needed.
Another approach would be to consider that each member of the BindingLayout is an entry in the layout's Children collection. With this knowledge, you would only need to hide the BoxView on the last child in the Children collection.
var layout = [reference to the StackLayout];
layout.Children.Last().IsVisible = false; // add using for System.Linq
Besides hiding the BoxView with the IsVisible property, you can also hide it by getting a reference to the grid ColumnDefinition and setting the Width to 0. This can be useful when there is more than one control to hide.
Related
I'm creating application with chat function. I made simple 'typing indicator' that shows new item in collectionView (Label) with text 'User is typing...'. So now i would like to animate this Label or add some animation like 3 dots that indicates user is typing. Is it somehow possible to make something like this? Any help appreciated, Thank you.
#EDIT
Code below describes my Chat layout:
<ContentPage.Content>
<StackLayout x:Name="content_stackLayout">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<CollectionView
x:Name="chat_collectionView"
Grid.Row="0"
VerticalOptions="Start"
HorizontalOptions="Center"
ItemSizingStrategy="MeasureAllItems"
SelectionMode="None"
ItemsSource="{Binding Messages}"
ItemsUpdatingScrollMode="KeepLastItemInView"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="{Binding messageAlignment}" Margin="30,5,30,5">
<Image
Source="{Binding image}"
Margin="{Binding imageMargin}"
HeightRequest="40"
WidthRequest="40"
Aspect="AspectFit" />
<Frame
BorderColor="Black"
CornerRadius="{Binding cornerRadius}"
BackgroundColor="{Binding kolorWiadomosci}"
Margin="{Binding messageMargin}">
<Label
Text="{Binding message}"
FontAttributes="{Binding textAttribute}"
x:Name="label"
FontSize="Title"
VerticalOptions="CenterAndExpand" />
</Frame>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<StackLayout
Grid.Row="1"
Orientation="Horizontal"
x:Name="sendMessage_stackLayout"
HeightRequest="80"
Padding="8"
>
<Entry
Margin="10,10,0,5"
Placeholder="your message..."
ClassId="message_entry"
x:Name="message_entry_label"
HorizontalOptions="FillAndExpand"
ClearButtonVisibility="WhileEditing"
Keyboard="Chat"
ReturnType="Send"
TextColor="Black"
BackgroundColor="#2596be"
/>
<Button
WidthRequest="200"
HeightRequest="30"
Text="Send"
FontSize="Title"
x:Name="button_send_label"
HorizontalOptions="End"
Clicked="Button_Clicked"
BorderWidth="5"
BorderColor="Black"
CornerRadius="5"
/>
</StackLayout>
</Grid>
</StackLayout>
</ContentPage.Content>
I've got binding with viewModel which receives messages from another App with SignalR, creates a new messages and adds them to List called "Messages" like below:
public class Message
{
public string message{ get; set; }
//public Thickness messageMargin{ get; set; }
public string image{ get; set; }
//public Thickness imageMargin{ get; set; }
public FontAttributes textAttribute{ get; set; }
public string author{ get; set; }
public LayoutOptions messageAlignment{ get; set; }
public Color textColor{ get; set; }
public double cornerRadius { get; set; }
}
So now when another person in second App is typing, it fires SignalR boolean method that my 'Chat App' receives. If UserTyping = true - App adds new message saying 'User is typing'. I would like to animate it somehow or if not possible, develop another solution for this behaviour.
Hy,
I am trying to show a comment input if the item checkbox is checked and hide it else, i have this XAML
<ListView ItemsSource="{Binding TaskItems}" x:Name="TasksItems" HasUnevenRows="True" VerticalScrollBarVisibility="Default" SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout IsVisible="True" Orientation="Vertical">
<Grid BackgroundColor="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<StackLayout Grid.Column="0" Grid.Row="0">
<input:CheckBox Type="Box" IsChecked="{Binding TaskChecked , Mode=TwoWay}"/>
</StackLayout>
<StackLayout Grid.Column="0" Grid.Row="1" IsVisible="{Binding CommentRequired}">
<Entry BackgroundColor="White" PlaceholderColor="Black" HeightRequest="40" TextColor="Black"/>
</StackLayout>
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
and i have this c# code to read the items from database
public class TaskData
{
public bool TaskChecked { get; set; }
public bool CommentRequired { get; set; }
}
public partial class HomePage : ContentPage
{
public HomePage()
{
ObservableCollection<TaskData> TaskItems { get; set; }
ObservableCollection<TaskData> TasksList;
TaskItems = new ObservableCollection<TaskData>();
//Loop the tasks from database result
While...
TaskItems.Add(new TaskData
{
TaskChecked = false,
CommentRequired = false,
});
//End Loop
TasksListView.ItemsSource = TaskItems;
}
}
Now i need to add a "CheckChanged" event to show the comment Entry (IsVisible="True") when the user check the checkbox of the targed listview item
Thanks
First add the event.
<input:CheckBox Type="Box" IsChecked="{Binding TaskChecked , Mode=TwoWay}" CheckedChanged="OnCheckBoxCheckedChanged"/>
Add Name for the second stack
<StackLayout x:Name="StackLayoutEntry" Grid.Column="0" Grid.Row="1" IsVisible="{Binding CommentRequired}">
<Entry BackgroundColor="White" PlaceholderColor="Black" HeightRequest="40" TextColor="Black"/>
</StackLayout>
Then in code Behind use this function to find the entry for the clicked checkbox
void OnCheckBoxCheckedChanged(object sender, CheckedChangedEventArgs e)
{
var Sender = (CheckBox)sender;
var stacklayoutentry = Sender.Parent.FindByName<StackLayout>("StackLayoutEntry");
stacklayoutentry.IsVisible = True;
}
This might also help you also Check
Another approach but you an identifier to your selected item.
Check
I have a XAML view in which I am binding to a ViewModel and an ObservableCollection (Games) of type GAME_TBL
<ListView x:Name="GameListView"
ItemsSource="{Binding Games}"
ItemTapped="Handle_ItemTapped"
CachingStrategy="RecycleElement"
RowHeight="120">
I am referencing properties of that GAME_TBL object like so
<Label Text="{Binding GAME_NAME}"
Style="{StaticResource GameListTitle}" />
However, I want to style the list rows and tried to bind to an object that is not a property of GAME_TBL
<BoxView Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="5"
Grid.RowSpan="5"
BackgroundColor="{Binding BoxViewStyle}"/>
Code behind from same ViewModel
public Color BoxViewStyle
{
get { return _boxViewStyle; }
set
{
_boxViewStyle = value;
OnPropertyChanged(nameof(BoxViewStyle));
}
}
When the ViewModel is called I then set it like this
BoxViewStyle = Color.FromHex("#000000");
However it hasn't worked
I think it's something to do with me declaring the entire ListView to have an ItemSource which is the OS, but then trying to use an object outside of that without explicitly referencing it? Might be wrong about that.
The BindingContext for your list view is whatever data type Games is. Since the BoxViewStyle property lives in your ViewModel you can't bind to it from inside your ListView.ItemTemplate. You need to specify the source for your Binding.
Name your main ContentPage element. x:Name="mainElement"
When you set your BoxViewStyle binding specify the source:
<BoxView Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="5"
Grid.RowSpan="5"
BackgroundColor="{Binding BoxViewStyle, Source={x:Reference mainElement}"/>
no that was just to get things working stage by stage - I wanted to know I could bind the color first, then I was going to write a method that would alternate the colours every row
If you want to have the same color for ListView row, you can create BoxViewStyle color property in ViewModel, as ottermatic said that BoxViewStyle property is in ViewModel, so you can not bind it for ListView datetemplate, so you name your listview as list firstly, find list's BindingContext.BoxViewStyle.
<ListView x:Name="list" ItemsSource="{Binding games}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding GAME_NAME}" />
<BoxView BackgroundColor="{Binding BindingContext.BoxViewStyle, Source={x:Reference list}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want to alternate the colors every ListView row, I suggest you can create BoxViewStyle in model, according to ListView row index to change color.
<ListView x:Name="list" ItemsSource="{Binding games}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding GAME_NAME}" />
<BoxView BackgroundColor="{Binding BoxViewStyle}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Model.cs:
public class Game
{
public int Index { get; set; }
public string GAME_NAME { get; set; }
public Color BoxViewStyle
{
get
{
if (Index % 2 == 0)
{
return Color.Red;
}
else
{
return Color.Blue;
}
}
}
}
ViewModel.cs:
public class GameViewModel
{
public ObservableCollection<Game> games { get; set; }
public GameViewModel()
{
games = new ObservableCollection<Game>()
{
new Game(){Index=0,GAME_NAME="game 1"},
new Game(){Index=1,GAME_NAME="game 2"},
new Game(){Index=2,GAME_NAME="game 3"},
new Game(){Index=3,GAME_NAME="game 4"},
new Game(){Index=4,GAME_NAME="game 5"}
};
}
}
If my reply solved your issue, please remember to mark my reply as answer, thanks.
How do we bind data to a footer inside ListView in Xamarin Forms, here I would need to pass the count_in value to footer.
<ListView x:Name="listView">
<ListView.Footer>
<StackLayout>
<Label Text="{Binding Count}" BackgroundColor="Gray"></Label>
</StackLayout>
</ListView.Footer>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding image}" WidthRequest="50" HeightRequest="50" Grid.Column="0" VerticalOptions="Center"/>
<StackLayout Grid.Column="1">
<Label Text="{Binding FullName}" TextColor="#f35e20" HorizontalTextAlignment="Center"/>
</StackLayout>
<StackLayout Grid.Column="2">
<Label Text="{Binding SoccerStatus}" HorizontalTextAlignment="Center" TextColor="#503026"/>
</StackLayout>
<StackLayout Grid.Column="3">
<Label Text="{Binding CurrentDate}" HorizontalTextAlignment="Center" TextColor="#503026"/>
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Below is the DisplayCount() gets the count from database;
public void DisplayCount()
{
var datetoday = DateTime.Now.ToString("ddMMyyyy");
var count_in = (from x in conn.Table<SoccerAvailability>().Where(x => string.Equals(x.SoccerStatus, "IN", StringComparison.OrdinalIgnoreCase) && x.CurrentDate == datetoday) select x).Count();
}
you are binding to Count
<Label Text="{Binding Count}" BackgroundColor="Gray"></Label>
so Count needs to be a public property on your ViewModel
public int Count { get; set; }
Now getting database exception SQLite exception no such function
equals
This is because SQLLite linq doesn't recognize the string.Equals method. You could convert it to list using ToListAsync for one condition. Then filter the c# list object using equals:
var datetoday = DateTime.Now.ToString("ddMMyyyy");
var items = await conn.Table<SoccerAvailability>().Where(x => x.CurrentDate == datetoday).ToListAsync();
var finalsItems = items.Where(x => string.Equals(x.SoccerStatus, "IN", StringComparison.OrdinalIgnoreCase)).ToList();
Count = finalsItems.Count();
At last, binding your Lable's Text to this Count property.
Edit about binding:
Have you set your content page to your ViewModel? Moreover, implement the INotifyPropertyChanged interface in your view model:
// Set your page's binding context
BindingContext = new PageViewModel();
public class PageViewModel : INotifyPropertyChanged
{
int count;
public int Count
{
set
{
if (count != value)
{
count = value;
onPropertyChanged();
}
}
get
{
return count;
}
}
public event PropertyChangedEventHandler PropertyChanged;
void onPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Edit2:
If you didn't use view model, set a name to your footer label:
<ListView.Footer>
<StackLayout>
<Label x:Name="FooterLabel" BackgroundColor="Gray"></Label>
</StackLayout>
</ListView.Footer>
Then set its value directly:
//...
FooterLabel.Text = finalsItems.Count().ToString();
You need to specify the binding context for the footer.
By default for footers, headers, and templates in a list view, its binding context is going to be the item currently being displayed. So in this case Count would need to be a public property on the item from ItemsSource. Point the Count binding at your view model instead.
<Label Text="{Binding BindingContext.Count, Source={x:Reference xNameSetForCurrentPage}" BackgroundColor="Gray"></Label>
xNameSetForCurrentPage is the x:Name="name" set at the top most element(where all the xmlns stuff is) of the page.
im trying to keep a searchbar for datagrid,so when the user enters certain value in search bar,the datagrid should show only entered value.i installed xamarin.forms.xamarin.i have gone through different example but no luck i did get what iam looking for.Looking forward for positive responce.
Thank you
This can be achieved by putting a SearchBar above a ListView. Binding the Text value of the SearchBar to a property in your ViewModel enables you to handle changes to the Text property by querying the data.
In XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<SearchBar Grid.Row="0" Text="{Binding Filter}" HeightRequest="40" />
<ListView Grid.Row="1" ItemsSource="{Binding Items}" />
</Grid>
In the ViewModel:
public List<MyObject> Items { get; set; }
public string Filter
{
get { return filter; }
set
{
filter = value;
// Apply filter to list of Items here...
}
}