In my Xamarin Forms application, I have a list of strings (minimum 1, maximum 4) that I want to display evenly in a column layout. Each column needs to have the same width and the content should expand so that the whole text is wrapped and visible. I know how I can do it using the Grid control using Width="*" on its columns.
I want to achieve the same result using FlexLayout, so I can bind the list of strings to BindableLayout and easily add and remove columns (I will not be displaying strings but a more complex layout in each column).
Using FlexLayout.Grow and FlexLayout.Basis I can get the FlexLayout to display evenly sized columns. The problem is making the FlexLayout's height fit all the displayed labels. Only the first row of text is displayed.
Both the Grid and FlexLayout are wrapped in a StackLayout:
<StackLayout>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="First label" Grid.Column="0" BackgroundColor="Aqua"
LineBreakMode="WordWrap" />
<Label Text="Second label" Grid.Column="1" BackgroundColor="Red"
LineBreakMode="WordWrap" />
<Label Text="Third label but with longer text" Grid.Column="2" BackgroundColor="Aqua"
LineBreakMode="WordWrap"/>
<Label Text="Fourth label" BackgroundColor="Red"
Grid.Column="3" LineBreakMode="WordWrap" />
</Grid>
<BoxView BackgroundColor="Blue"></BoxView>
<FlexLayout AlignItems="Stretch">
<Label Text="First label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Aqua"
LineBreakMode="WordWrap" />
<Label Text="Second label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Red"
LineBreakMode="WordWrap" />
<Label Text="Third label but with longer text"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Aqua"
VerticalOptions="FillAndExpand"
LineBreakMode="WordWrap" />
<Label Text="Fourth label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Red"
LineBreakMode="WordWrap" />
</FlexLayout>
<BoxView BackgroundColor="Blue"></BoxView>
</StackLayout>
Grid and FlexLayout displayed
I figured out that when setting the HeightRequest of the FlexLayout to a specific number (e.g. 150), everything works as expected - the row has a height of 150 and all the labels stretch out to fit that. So what I need is somehow specify HeightRequest="Auto" so that the row fits all the column's content without being set to a specific value
Is there a way to achieve this?
FlexLayout will cut its child Elements . So in your case use Grid is the best solution .
so I can bind the list of strings to BindableLayout and easily add and remove columns
If you want to display a collection of data I suggest that you could use ListView or CollectionView(if you want to let the collection scroll in Horizontal or display multi Columns in the same row) .
<ContentPage.Resources>
<ResourceDictionary>
<local:WidthConverter x:Key="WidthConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout x:Name="stack">
<CollectionView x:Name="list" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid WidthRequest="{Binding Source={x:Reference stack},Path=Width,Converter={StaticResource WidthConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Text="111111" BackgroundColor="LightBlue" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
in code behind
public class WidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var width = (double)value;
if(width>0)
{
return width * 0.25;
}
return 100;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return 0;
}
}
For more details you could check https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout#horizontal-list
How can I remove the row Comment is on if its null or empty string?
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding EntryDate}" Grid.Column="0" Grid.Row="0"></Label>
<Label Text="{Binding Sleep}" Grid.Column="1" Grid.Row="0"></Label>
<Label Text="{Binding Comment}" Grid.Column="0" Grid.ColumnSpan="4"
Grid.Row="1"></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
This is the appropriate case to use a ValueConverter.
All you need to do is create a converter like this:
namespace App.Converters
{
public class TextToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(value != null)
if (!(value is string)) return true;
return string.IsNullOrWhiteSpace(value as string) ? false : true;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And use it on the IsVisible property of Label:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:App.Converters"
x:Class="App.Views.SamplePage">
<ContentPage.Resources>
<ResourceDictionary>
<converters:TextToBoolConverter x:Key="TextToBoolConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding EntryDate}" Grid.Column="0" Grid.Row="0"></Label>
<Label Text="{Binding Sleep}" Grid.Column="1" Grid.Row="0"></Label>
<Label Text="{Binding Comment}" Grid.Column="0" Grid.ColumnSpan="4"
Grid.Row="1"
IsVisible={Binding Comment, Converter={StaticResource TextToBoolConverter}}/>
</Grid>
</ContentPage>
Thus you can reuse it wherever you need.
Your model:
public class MyModel
{
//...other properties ...
public string Comment { get; set; }
public bool HasComment => !string.IsNullOrEmpty(Comment);
}
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding EntryDate}" Grid.Column="0" Grid.Row="0"></Label>
<Label Text="{Binding Sleep}" Grid.Column="1" Grid.Row="0"></Label>
<Label Text="{Binding Comment}" Grid.Column="0" Grid.ColumnSpan="4"
Grid.Row="1" IsVisible = {Binding HasComment}></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
In my UWP app which uses MVVMLight, I have a button contained in my ListViewItem and when clicked I want to display a flyout and provide additional actions for the specific ListViewItem but whenever I tap on the button, I get an error i.e.
System.InvalidCastException: Unable to cast object of
type 'Windows.UI.Xaml.Input.TappedRoutedEventArgs' to
type 'MyApp.ViewModels.ItemViewModel'.
at GalaSoft.MvvmLight.Command.RelayCommand`1.Execute(Object parameter)
at Microsoft.Xaml.Interactions.Core.InvokeCommandAction.Execute(Object
sender, Object parameter) at
Microsoft.Xaml.Interactivity.Interaction.ExecuteActions(Object sender,
ActionCollection actions, Object parameter)
at Microsoft.Xaml.Interactions.Core.EventTr
I understand to some extend why it's happening but how I can I fix this? The button contained in my ListViewItem data template is obviously being passed to the parent which is the listview and the DataTrigger that's defined in the Listview is capturing it.
All I want to do is display a flyout when this button is clicked to provide additional options.
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="{StaticResource SystemControlBackgroundAccentBrush5}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="4"
Width="90"
Height="90"
VerticalAlignment="Center"
Margin="5,5,0,5">
<Image Width="90"
Height="90"
Source="{Binding Logo}" />
</Border>
<Grid Grid.Row="0" Grid.Column="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Row="1" VerticalAlignment="Bottom">
<TextBlock Text="Id:" FontWeight="SemiBold" Foreground="White" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Subject}" Margin="10,0,0,0" Foreground="White" VerticalAlignment="Center"/>
</StackPanel>
<Button Width="30" Height="30"
Command="{Binding AdditionalInfoCommand}"
Grid.RowSpan="4"
Grid.Column="1"
Margin="0,0,12,0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Stroke="{ThemeResource SystemControlBackgroundAccentBrush}"
Fill="{ThemeResource SystemControlBackgroundAccentBrush}"
StrokeThickness="2">
</Ellipse>
<Image Source="ms-appx:///Assets/Information.png" Width="30" Height="30" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
and my ListView has the following code:
<ListView ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
.... as defined above
</ListView.ItemTemplate>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ListView>
Any ideas on how I can resolve this?
UPDATE 1:
I'll re-phrase my question slightly. I've managed to bind the button displayed on the ListViewItem to a specific click event by introducing a RelayCommand (AdditionalInfoCommand) in the item's ViewModel. So now my main problem is the actual error mentioned above which is triggered after AdditionalInfoCommand and I assume it's because it is being captured by the ItemClickCommand. Strange as you would assume that it is using the same ViewModel.
Is there a way to no trigger the itemclick when this button is tapped?
Thanks.
Thierry
I figured it out.
When I tap the button inside my ListView DataTemplate, the button is being passed the Item's ViewModel which is correct but then the tap event is bubbled up back to the parent which is captured by:
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ItemClickCommand}"
CommandParameter="{Binding SelectedItem}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
While there may be a solution to stop the event bubbling, to circumvent the problem, I changed my ItemClickCommand.
It was defined as follows:
private RelayCommand<MyItemViewModel> _itemClickCommand;
public RelayCommand<MyItemViewModel> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<MyItemViewModel>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I simply changed it to:
private RelayCommand<object> _itemClickCommand;
public RelayCommand<object> ItemClickCommand
{
get { return this._itemClickCommand ?? (this._itemClickCommand =
new RelayCommand<object>((viewModel) =>
ItemClickCommandAction(viewModel))); }
}
I then changed the ItemClickCommandAction to take in an object instead of MyItemViewModel and now, within the method, I check the type of the ViewModel being passed:
private void ItemClickCommandAction(object currentObject)
{
if (currentObject is MyItemViewModel)
{
//go to next page
}
}
I have a listview defined like this:
<ListView
x:Name="phonesListView"
Grid.Row="5"
Background="Black"
IsItemClickEnabled="True"
Foreground="Gray" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="auto" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid
Grid.Row="0">
<Grid.ColumnDefinitions >
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ComboBox
Grid.Column="0"
VerticalAlignment="Center"
Background="Black"
Foreground="White">
<ComboBoxItem Content="Home" IsSelected="True"/>
<ComboBoxItem Content="{Binding Type}"/>
<ComboBoxItem Content="Office"/>
<ComboBoxItem Content="Fax"/>
</ComboBox>
<Button
Grid.Column="1"
Height="30"
Width="30"
Foreground="Black"
Margin="0, 5, 0, 5"
HorizontalAlignment="Center" Click="RemovePhone">
<Button.Background>
<ImageBrush Stretch="Fill" ImageSource="Assets/appbar.close.png" />
</Button.Background>
</Button>
</Grid>
<TextBox
Grid.Row="1"
Background="White"
Foreground="Black"
FontSize="20"
InputScope="TelephoneNumber"
Text="{Binding Number}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have an private ObservableCollection<Phone> numbers = new ObservableCollection<Phone>();
In constructor I call phonesListView.ItemSource = numbers;
And on some button click I want to add new item to the listView so I call a method:
private void AddPhone(object sender, RoutedEventArgs e) {
Phone phone = new Phone("", Types.HOME);
numbers.Add(phone);
}
But after button click to add a item the app crashes and App.g.i.cs is called and global::System.Diagnostics.Debugger.Break(); is highlighted
#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException += (sender, e) =>
{
if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
};
#endif
I'm really new to Universal Windows App, and I read that this is called when something is wrong with XAML code. Could you help me with that? Thanks.
I tried to use ItemsSource="{x:Bind Mode=TwoWay}"
The only way (but not the best, I think) where I could dynamically update a listview was making a method that handle a button event where I add a new element (calling the addmethod) and updating the ItemsSource property of the list view.
private void AnadeDoctorAppBarButton_Click(object sender, RoutedEventArgs e)
{
Doctor A = new Doctor("Mapache", "dentista", "La calle", "La vida", "Lo que sea", "Direccion", "olos");
ViewModel.AnadeDoctor = A;
ListaDePrueba.ItemsSource = ViewModel.ColeccionDoctores;
}
It Works, but I think that is not true Databinding.
In the first block below I intentionally miss named one of my binding statements so I could compare to my second block. The difference is in the property not found on 'ag2.item...' line.
item is my model.
In block 2 you can see that it is pointing to my view model (ag2.viewModel.itemViewModel).
What do I need to do in my XAML or my codebehind to get it to point to my class rather than the viewmodel?
Block 1:
BindingExpression path error: 'itemModel1' property not found on
'ag2.item, ag2, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null'. BindingExpression: Path='itemModel1'
DataItem='ag2.item, ag2, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null'; target element is
'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is
'Text' (type 'String')
Block 2:
BindingExpression path error:'itemModel' property not found on
'ag2.viewModel.itemViewModel, ag2, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null'. BindingExpression: Path='itemModel'
DataItem='ag2.viewModel.itemViewModel, ag2, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null'; target element is
'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is
'Source' (type 'String')
Code behind to Block 2:
itemViewModel VM = new itemViewModel((Int32)navigationParameter);
DataContext = VM;
I should also note that in block 1 I am doing my binding to a GridView where the ItemSource="{Binding item}" is set.
In block 2 I built my UI using grids and textblocks using this: Text="{Binding Path=itemModel}"
Update: In an effort to try to gain better understanding. I'm putting my code out there: Here is the XAML, below that will be the ViewModel and below that is my Model... I'm new at MVVM so I really don't know what I'm doing incorrectly. Any help is greatly appreciated.
XAML:
<common:LayoutAwarePage
x:Name="pageRoot"
x:Class="autoGarage2.VehicleItemDetailPage"
IsTabStop="false"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="using:autoGarage2.Common"
xmlns:local="using:autoGarage2"
xmlns:data="using:autoGarage2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Style="{StaticResource LayoutRootStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<!-- Back button and page title -->
<Grid
Style="{StaticResource LayoutRootStyle}" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource PageHeaderTextStyle}" Grid.Column="1"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Width="auto" Margin="50,0,0,0" VerticalAlignment="Top" >
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border Background="White" BorderBrush="CornflowerBlue" BorderThickness="1">
<Image Source="{Binding Image}" Margin="50"/>
</Border>
<StackPanel VerticalAlignment="Bottom" Background="CornflowerBlue">
<StackPanel Orientation="Horizontal" DataContext="{Binding vehicles}">
<TextBlock Text="{Binding VehicleMake}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource PageSubheaderTextStyle}" Margin="5"/>
<TextBlock Text="{Binding VehicleModel}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource PageSubheaderTextStyle}" Margin="5"/>
</StackPanel>
</StackPanel>
</Grid>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1">
<Grid Margin="20,0,0,20">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Vehicle Make:" Grid.Row="0" Grid.Column="0" FontSize="25" Foreground="Black" />
<TextBox Text="{Binding Path=VehicleMake}" Grid.Row="0" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
<TextBlock Text="Vehicle Model:" FontSize="25" Foreground="Black" Grid.Row="1" Grid.Column="0"/>
<TextBox Text="{Binding VehicleModel}" Grid.Row="1" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
<TextBlock Text="Vehicle Year:" FontSize="25" Foreground="Black" Grid.Row="2" Grid.Column="0"/>
<TextBox Text="{Binding VehicleYear}" Grid.Row="2" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
<TextBlock Text="License Plate:" FontSize="25" Foreground="Black" Grid.Row="3" Grid.Column="0"/>
<TextBox Text="" Grid.Row="3" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
<TextBlock Text="VIN #" FontSize="25" Foreground="Black" Grid.Row="4" Grid.Column="0"/>
<TextBox Text="" Grid.Row="4" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1" />
<TextBlock Text=" Current Mi/Km" FontSize="25" Foreground="Black" Grid.Row="5" Grid.Column="0"/>
<TextBox Text="" Grid.Row="5" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
<TextBlock Text="" FontSize="25" Foreground="Black" Grid.Row="6" Grid.ColumnSpan="2"/>
<TextBlock Text="Last Oil Change" FontSize="25" Foreground="Black" Grid.Row="7" Grid.Column="0"/>
<TextBox Text="" Grid.Row="7" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
<TextBlock Text="Last Oil Change Mi/Km" FontSize="25" Foreground="Black" Grid.Row="8" Grid.Column="0"/>
<TextBox Text="" Grid.Row="8" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
<TextBlock Text="" FontSize="25" Foreground="Black" Grid.Row="9" Grid.ColumnSpan="2"/>
<TextBlock Text="Reminder Mi/Km" FontSize="25" Foreground="Black" Grid.Row="10" Grid.Column="0"/>
<TextBox Text="" Grid.Row="10" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
<TextBlock Text="Reminder Month(s)" FontSize="25" Foreground="Black" Grid.Row="11" Grid.Column="0"/>
<TextBox Text="" Grid.Row="11" Grid.Column="1" FontSize="25" BorderBrush="CornflowerBlue" BorderThickness="1"/>
</Grid>
</StackPanel>
</Grid>
</Grid>
</common:LayoutAwarePage>
View Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using Windows.Foundation.Collections;
using System.IO;
namespace autoGarage2.viewModel
{
class vehicleViewModel
{
private IList<vehicle> m_vehicles;
private IList<vehicle> m_vehicleItem;
public IList<vehicle> vehicles
{
get { return m_vehicles; }
set { m_vehicles = value; }
}
public IList<vehicle> vehicleItem
{
get { return m_vehicleItem; }
set { m_vehicleItem = value; }
}
private IList<vehicle> getVehicleDetail(Int32 vId)
{
var vehicleItem =
from v in vehicles
where v.VehicleId == vId
select v;
if (vId > 0)
{
//vehicles.Clear();
m_vehicles = new List<vehicle>();
foreach (var item in vehicleItem)
{
m_vehicles = new List<vehicle>
{
new vehicle(item.VehicleId, item.VehicleMake.ToString(), item.VehicleModel.ToString(), item.VehicleYear, item.Image.ToString())
};
//vehicle myVehicle = new vehicle(item.VehicleId, item.VehicleMake.ToString(), item.VehicleModel.ToString(), item.VehicleYear, item.Image.ToString());
//m_vehicles.Add(myVehicle);
}
}
return m_vehicles;
}
public vehicleViewModel(Int32 vId)
{
m_vehicles = new List<vehicle>
{
new vehicle(1, "Mazda", "3", 2011, "Assets/car2.png"),
new vehicle(2, "Chevy", "Tahoe", 2004, "Assets/jeep1.png"),
new vehicle(3, "Honda", "Goldwing", 2007 ,"Assets/moto1.png")
};
if (vId > 0)
{
//m_vehicles = new List<vehicle>();
//m_vehicles =
//getVehicleDetail(vId);
m_vehicles = new List<vehicle>
{
new vehicle(2, "Chevy", "Tahoe", 2004, "Assets/jeep1.png"),
};
}
}
#region dbCode
//string dbName = "vehicle.db";
//var dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, dbName);
//using (var db = new SQLite.SQLiteConnection(dbPath))
// {
// var list = db.Table<vehicle>().ToList();
// m_vehicles = new List<vehicle>();
// for (Int32 i = 0; i < list.Count; i++)
// {
// //m_vehicles.Add(db.Table<vehicle>().ToList());
// }
// }
//foreach (vehicle item in m_vehicles)
//{
// AllItems.Add(item);
//}
#endregion
}
}
Model:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//using SQLite;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace autoGarage2
{
class vehicle : autoGarage2.Common.BindableBase
{
public vehicle()
{
}
public vehicle(string imagePath)
{
this._imagePath = imagePath;
}
public vehicle(Int32 vId, string vMake, string vModel, Int16 vYear, string imagePath)
{
this.m_vehicleID = vId;
this.m_vehicleMake = vMake;
this.m_vehicleModel = vModel;
this.m_vehicleYear = vYear;
this.m_vehicleName = vMake + " " + vModel;
this._imagePath = imagePath;
}
private Int32 m_vehicleID;
private String m_vehicleMake;
private String m_vehicleModel;
private Int16 m_vehicleYear;
private string m_vehicleName;
private ImageSource _image = null;
private String _imagePath = null;
private static Uri _baseUri = new Uri("ms-appx:///");
//[AutoIncrement, PrimaryKey]
public Int32 VehicleId
{
get
{
return m_vehicleID;
}
set
{
m_vehicleID = value;
OnPropertyChanged("VehicleId");
}
}
public String VehicleMake
{
get
{
return m_vehicleMake;
}
set
{
m_vehicleMake = value;
OnPropertyChanged("VehicleMake");
}
}
public String VehicleModel
{
get
{
return m_vehicleModel;
}
set
{
m_vehicleModel = value;
OnPropertyChanged("VehicleModel");
}
}
public Int16 VehicleYear
{
get
{
return m_vehicleYear;
}
set
{
m_vehicleYear = value;
OnPropertyChanged("VehicleYear");
}
}
public string VehicleName
{
get
{
return m_vehicleName;
}
set
{
m_vehicleName = value;
OnPropertyChanged("VehicleName");
}
}
public ImageSource Image
{
get
{
if (this._image == null && this._imagePath != null)
{
this._image = new BitmapImage(new Uri(vehicle._baseUri, this._imagePath));
}
return this._image;
}
set
{
this._imagePath = null;
this.SetProperty(ref this._image, value);
}
}
public void SetImage(String path)
{
this._image = null;
this._imagePath = path;
this.OnPropertyChanged("Image");
}
}
}
It sounds like you are trying to databind your TextBlock to a different object, not to the ViewModel (which is the DataContext of your window/control). If that is correct, you'll need to set the DataContext of the TextBlock or the parent Grid to the object you want to DataBind to.
The DataContext is used in determining the path to DataBind to for a control, and it inherits down the visual tree. So if your DataContext is set to MyViewModel, and you use Text="{Binding Path=itemModel}", your binding path will be MyViewModel.itemModel.
If you don't want to include MyViewModel in the binding path, you'll need to change the DataContext of the control in question, or of a containing control. For MVVM, this is often done by having the other object exposed as a property of the ViewModel. So if your MyViewModel had a property ItemModel:
public class ItemModel
{
public string Property1 { get; }
public string Property2 { get; }
}
public class MyViewModel
{
public ItemModel ItemModel { get; private set; }
}
Then your XAML could look like this (assuming MyViewModel is the DataContext of the parent window/control).
<Grid Grid.Row="1" DataContext="{Binding ItemModel}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding property1}"/>
<TextBlock Text="{Binding Property2}" Grid.Row="1"/>
</Grid>
And the two textboxes would be bound to Property1 and Property2 of the ItemModel object.
You seem to be trying to bind to a list but you are not using any ItemsControl in your XAML. You should probably use something like a ListView, bind its ItemsSource to your vehicles or vehicleItem lists, use an ItemTemplate/DataTemplate to define the look of each item in the collection, use ObservableCollection if your collection changes or raise INotifyPropertyChanged.PropertyChanged notifications if you swap out your collection, etc. Otherwise I would suggest reading something about binding ItemsControls or a general book about XAML, like Adam Nathan's WPF Unleashed. It seems like you are setting your m_vehicles only to replace it with a new one in the next statement. Also - you are setting the DataContext of the StackPanel to your list, which is allowed, but it does not work, since the DataContext of the elements of your StackPanel will still be the entire list and not its items, which is what you get by using an ItemsControl.
I was able to get my data to show up after a weekend of not looking at it. Basically I noticed that I was getting a binding error that stated that it couldn't find the property in my autoGarage2.vehicles list. So for grins I prefaced the binding like this:
{Binding vehicles[0].vehicleModel}
Next time I ran it, the data was there. After thinking about it some more, I decided to create a single object of vehicle and instead of populating the list object I'm populating just the single vehicle property. Now I'm doing something like this:
{Binding vehicleSingle.vehicleModel}
Thanks for every ones help. I guess this is just a nuance of how MVVM works in XAML...