Reputation: 155
I have a ListView with a button defined inside a ViewCell
. I would like to know how I can update this button's colour programmatically.
This is how my ListView is defined in XAML.
<ListView x:Name="HeaderListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Title}" TextColor="Black"/>
<Button x:Name="DetailButton" Grid.Row="1"
BackgroundColor="Blue"
TextColor="White"
Text="Details"
Clicked="DetailButton_Clicked"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The data source is a SQLite database that I set in C#:
protected override void OnAppearing()
{
base.OnAppearing();
using (SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation))
{
conn.CreateTable<Header>();
var header = conn.Query<Header>("select * from Header");
HeaderListView.ItemsSource = header;
}
}
The Detail button selects the row it is on, and navigates to a DetailPage constructed with the selected row's Header data. The DetailPage contains a second ListView, this one of a Detail table, which is linked to the Header table by a foreign key. Every row on the Detail table has a bool property of Complete
. The DetailPage's ListView's ItemsSource is set in the same way as the HeaderListView.
Some sample code to reflect what my tables look like:
public class Header
{
public int HeaderId { get; set; } // The primary key of the Header table
public string Code { get; set; } // Foreign key of the Detail table
// various Header data
public string Data1 { get; set; }
public DateTime Data2 { get; set; }
public int Data3 { get; set; }
}
public class Detail
{
public int DetailId { get; set; } // Primary key of Detail table
public string Code { get; set; } // Foreign key; Many Detail rows to one Header row
// various Detail data
public string Detail1 { get; set; }
public int Detail2 { get; set; }
public TimeSpan Detail3 { get; set; }
public bool Complete { get; set; }
}
The desired behaviour for my Detail button on the HeaderListView is as follows:
1) BackgroundColor="Blue"
when the Header has no matching Detail rows, thus there are no items in the DetailPage ListView.
2) BackgroundColor="Orange"
when the Header has at least one matching Detail row (and thus at least one item in the DetailPage ListView), and any rows' Complete
property is set to false
.
3) BackgroundColor="Green"
when the DetailPage has at least one matching row, and all of its' rows' Complete
properties are set to true
. If there is only a single row that matches the Header and its' Complete
property is true
, then the Detail button is still Green.
Please no suggestions for custom tools unless there is absolutely no way to do this with default Xamarin tools.
Upvotes: 0
Views: 533
Reputation: 3053
IIRC, I gave you a similar answer to a similar question before. You again should create a ViewModel inside your ViewModel.
public HeaderViewModel : BindableObject {
public Header Header { get; set; }
private ObservableCollection<Detail> details { get; set; }
public ObservableCollection<Detail> Details {
get => details;
set {
details = value;
OnPropertyChanged();
OnPropertyChanged(nameof(ButtonBackgroundColor);
}
}
public Color ButtonBackgroundColor {
get {
if (Details.Count == 0) return Color.Blue;
if (Details.Any(d => !d.Complete) return Color.Orange;
return Color.Green;
}
}
public HeaderViewModel(Header header) {
this.Header = header;
this.Details = SelectAllDetailItemsForWithThisHeaderId() //Your query that does what the name tells
}
}
Then in your main viewmodel, for every Header item, create a HeaderViewModel instance, put them in an ObservableCollection, and bind them as the ItemsSource of the ListView:
public class ViewModel : INotifyPropertyChanged {
private ObservableCollection<HeaderViewModel> headers { get; set; }
public ObservableCollection<HeaderViewModel> Headers {
get => headers;
set {
headers = value;
OnPropertyChanged();
}
}
//constructor
public ViewModel() {
List<Header> queriedHeaders = new List<Header>();
//please fetch data inside your viewmodel btw, currently you do it inside the view.
using (SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation))
{
conn.CreateTable<Header>();
queriedHeaders = conn.Query<Header>("select * from Header").ToList();
}
List<HeaderViewModel> _headers = new List<HeaderViewModel>();
foreach(Header h in queriedHeaders) {
var hvm = new HeaderViewModel(h);
_headers.Add(hvm);
}
this.Headers = new ObservableCollection<HeaderViewModel>(_headers);
}
}
Then in your XAML example above, change the two tags like this:
<ListView ItemsSource="{Binding Headers}">
<Button BackgroundColor="{Binding ButtonBackgroundColor}">
Upvotes: 1
Reputation: 15796
In your xaml, you should set binding to the backgroundColor of your button:
<Button BackgroundColor="{Binding btnColor}"/>
And in the code behind, set the value to btnColor
under different situations in your header
:
protected override void OnAppearing()
{
base.OnAppearing();
var header = conn.Query<Header>("select * from Header");
for (int i = 0; i < header.Count; i++)
{
Header h = header[i];
if (Header has no matching Detail rows)
{
h.btnColor = Color.Blue;
}
else if (your condition)
{
h.btnColor = Color.Orange;
}
else if (your condition) {
h.btnColor = Color.Green
}
else
{
h.btnColor = defaultColor;
}
}
HeaderListView.ItemsSource = header;
}
Add a btnColor property in your model:
public class Header
{
public int HeaderId { get; set; } // The primary key of the Header table
public string Code { get; set; } // Foreign key of the Detail table
// various Header data
public Color btnColor { get; set; }
}
Upvotes: 1
Reputation: 785
You could use styles with triggers in xaml.
Your viewmodel could have getters that return a colour/style for the column to use, you could then bind the column colours to that.
Or you could do it programmatically by assigning the header backgroundcolor based on the data like datagrid.columns[0].BackgroundColor = new SolidBrush("blue");
or something like that.
Upvotes: 1