I have problem because if I add new item in my observablecollection this i don't see result in my view.
I need a restart, then I can see the new item.
Here is my viewmodel where i dispaly items
public class ManageFleetListingViewModel : ViewModelBase
{
private readonly Func<IEnumerable<DisplayManageFleetViewModel>,IEnumerable<DisplayManageFleetViewModel>> _filtersVehicle;
private readonly ObservableCollection<DisplayManageFleetViewModel> _manageFleetViewModel;
private readonly VehicleState _vehicleState;
private readonly IManageFleetService _manageFleetService;
public IEnumerable<DisplayManageFleetViewModel> Vehicles => _manageFleetViewModel;
public ICommand DeleteVehicleCommand { get; set; }
public ManageFleetListingViewModel(VehicleState vehicleState, IManageFleetService manageFleetService) : this(vehicleState,manageFleetService, manageFleet => manageFleet) { }
public ManageFleetListingViewModel(VehicleState vehicleState, IManageFleetService manageFleetService, Func<IEnumerable<DisplayManageFleetViewModel>, IEnumerable<DisplayManageFleetViewModel>> filtersVehicle )
{
DeleteVehicleCommand = new DeleteVehicleCommand(this, manageFleetService);
_filtersVehicle = filtersVehicle;
_vehicleState = vehicleState;
_manageFleetViewModel = new ObservableCollection<DisplayManageFleetViewModel>();
_vehicleState.StateChanged += VehicleState_StateChanged;
DisplayVehicles();
}
public void DeleteItem(int id)
{
var item = Vehicles.FirstOrDefault(x => x.Id == id);
_manageFleetViewModel.Remove(item);
}
public void AddItem()
{
DisplayVehicles();
}
private void DisplayVehicles()
{
IEnumerable<DisplayManageFleetViewModel> displayManageFleets = _vehicleState.GetVehicles
.Select(s => new DisplayManageFleetViewModel(s.Id, s.CarBrand, s.VIN, s.Milage, s.EnigneNumber, s.EngineCapacity, s.RegistrationNumber, s.FirstRegistration, s.YearPurchase, s.YearProduction, s.ImageCar));
displayManageFleets = _filtersVehicle(displayManageFleets);
_manageFleetViewModel.Clear();
foreach (DisplayManageFleetViewModel viewModel in displayManageFleets)
{
_manageFleetViewModel.Add(viewModel);
}
}
private void VehicleState_StateChanged()
{
DisplayVehicles();
}
This is my project domain where I add item
public class ManageFleetService : IManageFleetService
{
private readonly IDataService<Account> _accountService;
private readonly IVehicleService _vehicleService;
public ManageFleetService(IDataService<Account> accountService, IVehicleService vehicleService)
{
_accountService = accountService;
_vehicleService = vehicleService;
}
public async Task<Account> AddVehicle(string carBrand, string vin, string milage, string engineNumber, string engineCapacity, string registerNumber, DateTime firstRegistration, DateTime yearPurchase, DateTime yearProduction, byte[] imageCar,Account accountId)
{
Vehicle vehicleVIN = await _vehicleService.GetByVIN(vin);
if(vehicleVIN != null)
{
throw new InvalidVinNumberException(vin);
}
Vehicle vehicleRegistraion = await _vehicleService.GetByRegistrationNumber(registerNumber);
if(vehicleRegistraion != null)
{
throw new InvalidRegistrationNumberException(registerNumber);
}
Vehicle vehicle = new Vehicle()
{
CarBrand = carBrand,
VIN = vin,
Milage = milage,
EnigneNumber = engineNumber,
EngineCapacity = engineCapacity,
RegistrationNumber = registerNumber,
FirstRegistration = firstRegistration,
YearPurchase = yearPurchase,
YearProduction = yearProduction,
ImageCar = imageCar,
Account = accountId
};
accountId.Vehciles = new List<Vehicle>();
accountId.Vehciles.Add(vehicle);
await _accountService.Update(accountId, accountId.Id);
return accountId;
}
When the item is added to the database i display next time functions DisplayVehicles in Command
Account account = await _manageFleetService.AddVehicle(carBrand, vin, milage, engineCapacity, engineCapacity, registerNumber, firstRegistration, yearPurchase, yearProduction, imageCar, _accountStore.CurrentAccount);
_manageFleetListingViewModel.AddItem();
This is my userconrol (ManageFleetListing) xaml where i display items
<Grid>
<StackPanel HorizontalAlignment="Center">
<ItemsControl ItemsSource="{Binding Vehicles}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Task:VehcileTask/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
Here is my pattern for VehicleTask
<StackPanel>
<Border>
</Border>
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
</Grid>
<StackPanel Grid.Column="0" MinWidth="150" MaxWidth="300">
<TextBlock Text="{Binding CarBrand}" FontSize="12" FontWeight="DemiBold"/>
</StackPanel>
<StackPanel Grid.Column="1" MinWidth="150" MaxWidth="300">
<TextBlock Text="{Binding VIN}" FontSize="12" FontWeight="DemiBold"/>
</StackPanel>
<StackPanel Grid.Column="2" MinWidth="100" MaxWidth="200">
<TextBlock Text="{Binding Milage}" FontSize="12" FontWeight="DemiBold"/>
</StackPanel>
<StackPanel Grid.Column="3" MinWidth="100" MaxWidth="200" Margin="0 0 20 0">
<TextBlock Text="{Binding YearProduction, StringFormat='dd/MM/yyyy'}" FontSize="12" FontWeight="DemiBold"/>
</StackPanel>
<Button Content="Edit" Command="{Binding Path=DataContext.DeleteVehicleommand, RelativeSource={RelativeSource AncestorType=local:ManageFleetListing}}"/>
<Button Background="Red" BorderThickness="0" Content="Delete" Command="{Binding Path=DataContext.DeleteVehicleCommand, RelativeSource={RelativeSource AncestorType=local:ManageFleetListing}}" Margin="10 0 0 0" CommandParameter="{Binding Id}"/>
</StackPanel>
<Border BorderThickness="1" Background="Black"></Border>
</StackPanel>
This is main view here i use CreateVehicleCommandand and display ManageFleetListing
<Button
Command="{Binding CreateVehicleCommand}"
Style="{DynamicResource InventoryButton}" Height="50" Width="200" HorizontalAlignment="Left" Margin="40 20 0 0">
<Button.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF5DFF00"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Button.Background>
<Button.Content>
<TextBlock Text="Add" FontSize="20" FontWeight="Bold" Foreground="Gray"/>
</Button.Content>
</Button>
<Grid Grid.Row="5" Height="200" Margin="0 10 0 0">
<ManageFleet:ManageFleetListing DataContext="{Binding ManageFleetListingViewModel}"/>
</Grid>
and this is view model for xaml above
public class ManageFleetViewModel : ViewModelBase
{
public ICommand CreateVehicleCommand { get; set; }
public ManageFleetListingViewModel ManageFleetListingViewModel { get; }
public ManageFleetViewModel(IManageFleetService menageFleetService, IAccountStore accountStore,VehicleState vehicleState, IManageFleetService manageFleetService,ManageFleetListingViewModel manageFleetListingViewModel)
{
CreateVehicleCommand = new CreateVehicleCommand(this, menageFleetService, accountStore, manageFleetListingViewModel);
ManageFleetListingViewModel = new ManageFleetListingViewModel(vehicleState,manageFleetService);
}
private string _carbrand { get; set; } //if is problem with added to database look here
private string _vin { get; set; }
private string _milage { get; set; }
private string _enigneNumber { get; set; }
private string _engineCapacity { get; set; }
private string _registrationNumber { get; set; }
private DateTime _firstRegistration { get; set; }
private DateTime _yearPurchase { get; set; }
private DateTime _yearProduction { get; set; }
private byte [] _imageCar { get; set; }
public string CarBrand
{
get
{
return _carbrand;
}
set
{
_carbrand = value;
OnPropertyChanged(nameof(CarBrand));
}
}
public string VIN
{
get
{
return _vin;
}
set
{
_vin = value;
OnPropertyChanged(nameof(VIN));
}
}
public string Milage
{
get
{
return _milage;
}
set
{
_milage = value;
OnPropertyChanged(nameof(Milage));
}
}
public string EnigneNumber
{
get
{
return _enigneNumber;
}
set
{
_enigneNumber = value;
OnPropertyChanged(nameof(EnigneNumber));
}
}
public string EngineCapacity
{
get
{
return _engineCapacity;
}
set
{
_engineCapacity = value;
OnPropertyChanged(nameof(EngineCapacity));
}
}
public string RegistrationNumber
{
get
{
return _registrationNumber;
}
set
{
_registrationNumber = value;
OnPropertyChanged(nameof(RegistrationNumber));
}
}
public DateTime FirstRegistration
{
get
{
if(_firstRegistration.Year == 1) { return DateTime.Now; }
return _firstRegistration;
}
set
{
_firstRegistration = value;
OnPropertyChanged(nameof(FirstRegistration));
}
}
public DateTime YearPurchase
{
get
{
if(_yearPurchase.Year == 1) { return DateTime.Now; }
return _yearPurchase;
}
set
{
_yearPurchase = value;
OnPropertyChanged(nameof(YearPurchase));
}
}
public DateTime YearProduction
{
get
{
if (_yearProduction.Year == 1) { return DateTime.Now; }
return _yearProduction;
}
set
{
_yearProduction = value;
OnPropertyChanged(nameof(YearProduction));
}
}
public byte [] ImageCar
{
get
{
return _imageCar;
}
set
{
_imageCar = value;
OnPropertyChanged(nameof(ImageCar));
}
}
Below is the accepted answer but as you can see from the comments my statements are not true.
You have to bind to an ObservableCollection<T> for the control to subscribe to changes to the collection. In your case you bind to IEnumerable<T> so the control is populated from this list once. Changes to the ObservableCollection<T> backing the IEnumerable<T> are never seen by the control.
public IEnumerable<DisplayManageFleetViewModel> Vehicles => _manageFleetViewModel;
Simply change this to
public ObservableCollection<DisplayManageFleetViewModel> Vehicles => _manageFleetViewModel;
Or perhaps even better get rid of the _manageFleetViewModel field and change Vehicles:
public ObservableCollection<DisplayManageFleetViewModel> Vehicles { get; }
Then use Vehicles instead of _manageFleetViewModel in your code.
Related
I have a list of tasks that I need to bind a Boolean variable to its item.
the task object includes a completedDate property that if it has value defines the task as completed.
on the view, I need to check if it has value the button text display the text: "mark as incomplete"
----Task Object-----
public class ProjectTaskLineItemSummary
{
public int TenantId { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal? CostMultiplier { get; set; }
public DateTimeOffset? CompletedDate { get; set; }
public int? CompletedByUserId { get; set; }
}
-------viewmodel-------
viewmodel()
{
public ObservableCollection<ProjectTaskLineItemSummary> Tasks { get; set; }
...
bool isCompleted;
public bool IsCompleted
{
get {
return isCompleted;
}
set
{
isCompleted = value;
OnPropertyChanged();
}
}
}
-----view----
<CollectionView Grid.Row="1" ItemsSource="{Binding Tasks}" x:Name="List3">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Frame
Margin="0,10"
Padding="10"
BackgroundColor="{StaticResource PrimaryWhite}"
BorderColor="{StaticResource PrimaryLightGray}"
CornerRadius="10"
HasShadow="False">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" RowSpacing="15">
<StackLayout HorizontalOptions="EndAndExpand" Orientation="Horizontal">
<Image
HeightRequest="20"
Source="iconCalender.png"
WidthRequest="20" />
<Label
FontFamily="{StaticResource MeduimFont}"
Style="{StaticResource LabelMedium}"
Text="{Binding CompletedDate,StringFormat='{0:MMMM dd, yyyy}'}"
TextColor="{StaticResource PrimaryBlack}"
/>
</StackLayout>
</StackLayout>
<BoxView
Grid.Row="1"
HeightRequest="1"
Color="{StaticResource PrimaryLightGray}" />
<Label
Grid.Row="2"
Style="{StaticResource LabelMedium}"
Text="{Binding Name}"
TextColor="{StaticResource PrimaryBlack}" />
<Button
x:Name="lstbtnMarkasComplite"
Grid.Row="5"
Padding="15,0"
Clicked="MarkTaskAsCompletedClicked"
CornerRadius="20"
FontSize="{StaticResource Font12}"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand"
Style="{StaticResource ButtonPurple}"
Text="Mark as Completed" >
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding IsCompleted}" Value="True">
<Setter Property="Text" Value="Mark Task as In Completed"/>
</DataTrigger>
</Button.Triggers>
</Button>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I tried to assign it in view model like below but it doesn't work:
public override async Task InitializeAsync(object navigationData)
{
await SetBusyAsync(async () =>
{
...
Tasks = ObjectMapper.Map<ObservableCollection<ProjectTaskLineItemSummary>>(project.TaskLineItems);
foreach (var task in Tasks)
{
isCompleted = task.CompletedDate.HasValue ? true : false;
}
RaisePropertyChanged(() => Model);
RaisePropertyChanged(() => Notes);
RaisePropertyChanged(() => Files);
RaisePropertyChanged(() => Tasks);
});
}
This could simply be achieved by Binding Text Property of the button and then dynamically set the Text of the button based on the CompletedDate of the entity.
Below is the code snippets for your reference:
Model:
ProjectTaskLineItemSummary.cs
public class ProjectTaskLineItemSummary
{
public int TenantId { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal? CostMultiplier { get; set; }
public DateTimeOffset? CompletedDate { get; set; }
public int? CompletedByUserId { get; set; }
public string CompletedButton { get; set; }
}
View:
<CollectionView ItemsSource="{Binding Tasks}" x:Name="List3" Background="aqua">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Frame Margin="0,10" Padding="10" HasShadow="False">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" RowSpacing="15">
<StackLayout HorizontalOptions="EndAndExpand" Orientation="Horizontal">
<Image HeightRequest="30" Source="XamarinLogo.png" WidthRequest="80" />
<Label x:Name="mydate" Text="{Binding CompletedDate,StringFormat='{0:MMMM dd, yyyy}'}" TextColor="Black"/>
</StackLayout>
<BoxView Grid.Row="1" HeightRequest="1" Color="Black" />
<Label Grid.Row="2" Text="{Binding Name}" TextColor="Black" />
<Button Grid.Row="5" x:Name="lstbtnMarkasComplite" Padding="15,0"
Clicked="MarkTaskAsCompletedClicked"
CornerRadius="20"
Text="{Binding CompletedButton}"
FontSize="Medium"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand">
</Button>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
ViewModel:
public class PageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<ProjectTaskLineItemSummary> Tasks { get; set; }
bool isCompleted { get; set; }
public bool IsCompleted
{
get => isCompleted;
set
{
isCompleted = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(IsCompleted)));
}
}
string completed { get; set; }
public String CompletedButton
{
get => completed;
set
{
completed = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(CompletedButton)));
}
}
public PageViewModel()
{
Tasks = new ObservableCollection<ProjectTaskLineItemSummary>()
{
new ProjectTaskLineItemSummary(){TenantId = 1, Id =1, Name = "jobs ", Description= "jjjj",CostMultiplier=1 ,CompletedDate =DateTime.UtcNow, CompletedByUserId=1 ,CompletedButton = ""},
new ProjectTaskLineItemSummary(){TenantId = 2, Id =2, Name = "james ",Description= "aaaa",CostMultiplier=2 , CompletedByUserId=2,CompletedButton = "" },
new ProjectTaskLineItemSummary(){TenantId = 3, Id =3, Name = "rollex ",Description= "rrrr",CostMultiplier=3 ,CompletedDate =DateTime.UtcNow, CompletedByUserId=3 ,CompletedButton = ""}
};
setButtonIsCompleted();
}
private void setButtonIsCompleted()
{
foreach (var task in Tasks)
{
if (task.CompletedDate == null)
{
task.CompletedButton = "Mark Task as Completed";
}
else
{
task.CompletedButton = "Mark Task as inCompleted";
}
}
}
}
I have a ListView with a ItemSource binding to a list object; inside the listview there are some items that are filled with the values of the list object. I have 2 checkboxes that are binding to elements of the list object and want to uncheck one when the other is checked. In the code I wrote, the values are correctly changed in the list object but the checkbox didn't change (stay unchecked). Next is the code that I wrote.
XAML Part
<ListView ItemsSource="{Binding ListaAsistencia}" HasUnevenRows="True" SelectionMode="None" x:Name="AsistList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Margin="5,2,5,2" Padding="0" BackgroundColor="Transparent" BorderColor="#915c0d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".7*"></ColumnDefinition>
<ColumnDefinition Width=".3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Text="{Binding alumno, Mode=TwoWay}" Grid.Row="0" Grid.Column="0" FontSize="Micro" Margin="5,2,2,2"/>
<Entry Placeholder="Notas" Text="{Binding notas}" Grid.Row="1" Grid.Column="0" FontSize="Micro" TextColor="Black" />
<Grid Grid.Row="0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".65*"></ColumnDefinition>
<ColumnDefinition Width=".35*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="AsistiĆ³" Grid.Column="0" HorizontalOptions="End" FontSize="Micro" VerticalOptions="Center"/>
<CheckBox IsChecked="{Binding asistencia, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="Black"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".65*"></ColumnDefinition>
<ColumnDefinition Width=".35*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="F. Just." Grid.Column="0" FontSize="Micro" HorizontalOptions="End" VerticalOptions="Center"/>
<CheckBox IsChecked="{Binding falta_justificada, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="DarkBlue" CheckedChanged="CheckBox_Just_CheckedChanged" AutomationId="{Binding idalumno_grupo}"/>
</Grid>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
C# BackEnd Part
private void CheckBox_Just_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
var vm = BindingContext as AsisCapturarViewModel;
if ((e.Value) && (!vm.obtainingData))
{
CheckBox switchBox = (CheckBox)sender;
vm.UncheckFalta(switchBox.AutomationId);
}
}
C# View Model Part
public async void UncheckFalta(string idalumno_grupo)
{
if (!String.IsNullOrEmpty(idalumno_grupo))
{
int idalumno_grupoUse = Convert.ToInt32(idalumno_grupo);
ListaAsistencia.Where(a => a.idalumno_grupo == idalumno_grupoUse).ToList().ForEach(s => s.asistencia = false);
}
}
Class used in the List
public class AsistenciaList
{
public int idasistencia { get; set; }
public DateTime fecha { get; set; }
public int idtipo_evento { get; set; }
public string tipo_evento { get; set; }
public int idmaestro_grupo { get; set; }
public int idalumno_grupo { get; set; }
public string alumno { get; set; }
public bool asistencia { get; set; }
public string notas { get; set; }
public bool falta_justificada { get; set; }
}
Thanks for your help.
Firstly , agree with Jason .You should implement the interface INotifyPropertyChanged in your model if you want to update UI in runtime .
In addition , since you have used MVVM , you should put all the logic to your viewmodel .
So you can improve your code as following
in your model
public class AsistenciaList:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int idasistencia { get; set; }
public DateTime fecha { get; set; }
public int idtipo_evento { get; set; }
public string tipo_evento { get; set; }
public int idmaestro_grupo { get; set; }
public int idalumno_grupo { get; set; }
public string alumno { get; set; }
public string notas { get; set; }
private bool asis;
public bool asistencia
{
get
{
return asis;
}
set
{
if (asis != value)
{
asis = value;
NotifyPropertyChanged();
}
}
}
private bool falta;
public bool falta_justificada
{
get
{
return falta;
}
set
{
if (falta != value)
{
falta = value;
NotifyPropertyChanged();
}
}
}
}
xaml
<CheckBox IsChecked="{Binding falta_justificada, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="DarkBlue" AutomationId="{Binding idalumno_grupo}"/>
ViewModel
//...
foreach(AsistenciaList asistencia in ListaAsistencia)
{
asistencia.PropertyChanged += Asistencia_PropertyChanged;
}
//...
private void Asistencia_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName== "falta_justificada")
{
AsistenciaList asistencia = sender as AsistenciaList;
var idalumno_grupo = asistencia.idalumno_grupo;
//...do something you want
}
}
I have a ListBox which is bound to a List of DataModel.
DataModel.cs
public class DataModel
{
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
}
The ListBox should display two properties, so I have defined the ItemTemplate as below
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding Code}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
My requirement is that the user can select which two properties to display in the ListBox at run time. I am not sure how to achieve this. I have created a sample solution to explain my problem.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Button Content="Go" Click="ButtonBase_OnClick" Margin="3"></Button>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding Code}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
}
}
public class ViewModel
{
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public ViewModel()
{
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1" });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2" });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3" });
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
}
public class DataModel
{
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
}
}
Things I have tried
<TextBlock Text="{Binding ElementName=ComboBox1, Path=SelectedItem}"></TextBlock>
This just displays the selected property name.
I suggest you to manage all the stuff regarding the "dynamic description" showed in the ListBox in your ViewModel.
First of all, your view model and your model should implement INotifyPropertyChanged. In my sample I created a simple base class which implements it. My base class is called NotifyPropertyChangedImpl.
Moreover I added two properties to the view model: the comboboxes for selecting the properties are binded to those two properties.
public class ViewModel : NotifyPropertyChangedImpl
{
private string property1;
private string property2;
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public string Property1
{
get
{
return property1;
}
set
{
if (property1 != value)
{
property1 = value;
SetDynamicDescriptions();
}
}
}
public string Property2
{
get
{
return property2;
}
set
{
if (property2 != value)
{
property2 = value;
SetDynamicDescriptions();
}
}
}
public ViewModel()
{
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1" });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2" });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3" });
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
private void SetDynamicDescriptions()
{
PropertyInfo propertyInfo1;
PropertyInfo propertyInfo2;
Type type = typeof(DataModel);
if (!String.IsNullOrEmpty(property1) && !String.IsNullOrEmpty(property2))
{
propertyInfo1 = type.GetProperty(property1);
propertyInfo2 = type.GetProperty(property2);
foreach (DataModel dataModel in DataModelList)
{
dataModel.DynamicDescription = String.Format("{0} - {1}",
propertyInfo1.GetValue(dataModel, null), propertyInfo2.GetValue(dataModel, null));
}
}
}
}
As you can see the method SetDynamicDescriptions rebuilds the DynamicsDescription each time Property1 or Property2 changes.
I also added a property to the model class:
public class DataModel : NotifyPropertyChangedImpl
{
private string dynamicDescription;
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
public string DynamicDescription
{
get
{
return dynamicDescription;
}
set
{
if (dynamicDescription != value)
{
dynamicDescription = value;
OnPropertyChanged("DynamicDescription");
}
}
}
}
So at the end your XAML will be:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"
SelectedItem="{Binding Property1}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"
SelectedItem="{Binding Property2}"></ComboBox>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DynamicDescription}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I hope this can help you.
In short, what I've done is I made 2 public properties to bind to and changed your properties to fields. Also I've made custom attribute, to control what fields user is able to select. And created a DisplayOptions class to store selection and spread it across DataModel instances.
The solution is a bit messy, considering it's a PoC, but I believe this can do:
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Button Content="Go" Click="ButtonBase_OnClick" Margin="3"></Button>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DisplayProperty1}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding DisplayProperty2}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
CS:
public partial class MainWindow : Window
{
public ViewModel model = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = model;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
//This part has to be on the View side, but this is PoC
model.Opts.Prop1 = typeof(DataModel).GetFields()
.Where(a => a.Name == ComboBox1.SelectedItem.ToString()).FirstOrDefault();
model.Opts.Prop2 = typeof(DataModel).GetFields()
.Where(a => a.Name == ComboBox2.SelectedItem.ToString()).FirstOrDefault();
DataContext = null;
DataContext = model;
}
}
public class ViewModel
{
public DisplayOptions Opts = new DisplayOptions();
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public ViewModel()
{
var properties = typeof(DataModel).GetFields()
.Where(a => a.CustomAttributes.Any(b => b.AttributeType == typeof(SwitchableAttr)));
//Initialising options before creating DataModel instances
DataModelProperties = properties.Select(s => s.Name).ToList();
Opts.Prop1 = properties.ElementAt(0);
Opts.Prop2 = properties.ElementAt(1);
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1", options = Opts });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2", options = Opts });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3", options = Opts });
}
}
public class DisplayOptions
{
public FieldInfo Prop1;
public FieldInfo Prop2;
}
public class DataModel
{
public DisplayOptions options;
[SwitchableAttr]
public string Name;
[SwitchableAttr]
public string Desc;
[SwitchableAttr]
public string Code;
public string DisplayProperty1 { get { return (string)options.Prop1.GetValue(this); } set { } }
public string DisplayProperty2 { get { return (string)options.Prop2.GetValue(this); } set { } }
}
public class SwitchableAttr : Attribute { }
This is not Complete MVVM
so here is the code
public partial class MainWindow : Window
{
private ViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
this.DataContext = vm;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(ComboBox1.Text) && !string.IsNullOrEmpty(ComboBox2.Text))
{
vm.AddDataToModel(ComboBox1.Text, ComboBox2.Text);
}
}
}
public class ViewModel
{
public List<String> DataModelProperties { get; set; }
private ObservableCollection<DataModel> _DataModelList;
public ViewModel()
{
_DataModelList = new ObservableCollection<DataModel>();
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
public void AddDataToModel(string cmbx1Val,string cmbx2Val)
{
_DataModelList.Add(new DataModel() { Name = cmbx1Val, Code = cmbx2Val, Desc = "Desc1" });
}
public ObservableCollection<DataModel> DataModelList
{
get
{
return _DataModelList;
}
set
{
_DataModelList = value;
}
}
}
I would like to show 6 values from the database in my corresponding page horizontally as for example
Date|Time|Floor|Zone|Latitude|longitude
But the page only shows
Data|Time|Floor|Zone
and does not show the latitude and longitude
Below is my Xaml code
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="Smart Parking" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="History" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="ListData">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name= "DateTxt" Text="{Binding Date}" TextWrapping="Wrap" />
<TextBlock x:Name= "TimeTxt" Text="{Binding Time}" TextWrapping="Wrap" />
<TextBlock x:Name= "ZoneTxt" Text="{Binding Zone}" TextWrapping="Wrap"/>
<TextBlock x:Name= "FloorTxt" Text="{Binding Floor}" TextWrapping="Wrap"/>
<TextBlock x:Name= "LatTxt" Text="{Binding location_latitude}" TextWrapping="Wrap" />
<TextBlock x:Name= "LongTxt" Text="{Binding location_longitude}" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
Can anyone please help me improve it or correct it ?
The code behind for list itmesource is here below
public partial class History : PhoneApplicationPage
{
// string dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "db.sqlite");
ObservableCollection<historyTableSQlite> DB_HistoryList = new ObservableCollection<historyTableSQlite>();
DbHelper add = new DbHelper();
public History()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
add.AddInfo();
ReadHistoryList_Loaded();
}
public void ReadHistoryList_Loaded()
{
ReadAllContactsList dbhistory = new ReadAllContactsList();
DB_HistoryList = dbhistory.GetAllHistory();//Get all DB contacts
ListData.ItemsSource = DB_HistoryList.OrderByDescending(i => i.Id).ToList();//Latest contact ID can Display first
}
here below is DBhelper class for all the main functions
public class DbHelper
{
SQLiteConnection dbConn;
public async Task<bool> onCreate(string DB_PATH)
{
try
{
if (!CheckFileExists(DB_PATH).Result)
{
using (dbConn = new SQLiteConnection(DB_PATH))
{
dbConn.CreateTable<historyTableSQlite>();
}
}
return true;
}
catch
{
return false;
}
}
private async Task<bool> CheckFileExists(string fileName)
{
try
{
var store = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
return true;
}
catch
{
return false;
}
}
//retrieve all list from the database
public ObservableCollection<historyTableSQlite> ReadHistory()
{
using (var dbConn = new SQLiteConnection(App.DB_PATH))
{
List<historyTableSQlite> myCollection = dbConn.Table<historyTableSQlite>().ToList<historyTableSQlite>();
ObservableCollection<historyTableSQlite> HistoryList = new ObservableCollection<historyTableSQlite>(myCollection);
return HistoryList;
}
}
// Insert the new info in the histrorytablesqlite table.
public void Insert(historyTableSQlite newcontact)
{
using (var dbConn = new SQLiteConnection(App.DB_PATH))
{
dbConn.RunInTransaction(() =>
{
dbConn.Insert(newcontact);
});
}
}
public void AddInfo()
{
DbHelper Db_helper = new DbHelper();
Db_helper.Insert((new historyTableSQlite
{
Date = DateTime.Now.ToShortDateString(),
Time = DateTime.Now.ToShortTimeString(),
Zone = "PST",
Floor = "10th Floor",
latitude = 35.45112,
longtitude = -115.42622
}));
}
}
and the last class for keeping the values
public class historyTableSQlite : INotifyPropertyChanged
{
[SQLite.PrimaryKey, SQLite.AutoIncrement]
public int Id { get; set; }
private int idvalue;
private string dateValue = string.Empty;
public string Date {
get { return this.dateValue; }
set
{
if (value != this.dateValue)
{
this.dateValue = value;
NotifyPropertyChanged("Date");
}
}
}
private string timeValue = string.Empty;
public string Time
{
get { return this.timeValue; }
set
{
if (value != this.timeValue)
{
this.timeValue = value;
NotifyPropertyChanged("Time");
}
}
}
private string floorValue = string.Empty;
public string Floor
{
get { return this.floorValue; }
set
{
if (value != this.floorValue)
{
this.floorValue = value;
NotifyPropertyChanged("Floor");
}
}
}
public string zoneValue;
public string Zone
{
get { return this.zoneValue; }
set
{
if (value != this.zoneValue)
{
this.zoneValue = value;
NotifyPropertyChanged("Zone");
}
}
}
private double latValue;
public double latitude
{
get { return latValue; }
set
{
if (value != this.latValue)
{
this.latValue = value;
NotifyPropertyChanged("Latitude");
}
}
}
private double lonValue;
public double longtitude
{
get { return this.lonValue; }
set
{
if (value != this.lonValue)
{
this.lonValue = value;
NotifyPropertyChanged("Longitude");
}
}
}
// public string isMarkPoint { get; set; }
public historyTableSQlite()
{
}
public historyTableSQlite(string date,string time,string floor,string zone,double lat,double lng)
{
Date = date;
Time = time;
Floor = floor;
Zone = zone;
latitude = lat;
longtitude = lng;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
You are binding your textblock to location_longitude but I don't see any location_longitude (or latitude) in your code behind.
Plus, you have a little misspelling in your Longitude and Latitude declarations
public double longtitude // Here change to Longitude
{
get { return this.lonValue; }
set
{
if (value != this.lonValue)
{
this.lonValue = value;
NotifyPropertyChanged("Longitude");
}
}
}
private double latValue;
public double latitude // Change to Latitude
{
get { return latValue; }
set
{
if (value != this.latValue)
{
this.latValue = value;
NotifyPropertyChanged("Latitude");
}
}
}
Try to bind your textblock as
<TextBlock x:Name="LatTxt" Text="{Binding Latitude}" TextWrapping="Wrap" />
<TextBlock x:Name="LongTxt" Text="{Binding Longitude}" TextWrapping="Wrap" />
Hope this help.
I can't bind an integer variable to the template.
My C# code looks like below:
class Task
{
public string name;
public string desc;
public int pr;
public string TaskName
{
get { return name; }
set { name = value; }
}
public string Description
{
get { return desc; }
set { desc = value; }
}
public int Priority
{
get { return pr; }
set { pr = value; }
}
public Task(string name, string description, int pr)
{
this.TaskName = name;
this.Description = description;
this.Priority = pr;
}
}
and the XAML code is
<DataTemplate x:Key="myTaskTemplate">
<Border Name="border" BorderBrush="DarkSlateBlue" BorderThickness="2"
CornerRadius="2" Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Padding="0,0,5,0" Text="Task Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Padding="0,0,5,0" Text="Description:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Padding="0,0,5,0" Text="Priority:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
</Grid>
</Border>
</DataTemplate>
There is always "0" for the Priority column now. The other binding variables are displayed correctly, but they are strings.
Ussualy the ViewModel should implement INotifyPropertyChanged in order to propagate changes in properties to the view.
This being said, your class should look like this:
class Task : INotifyPropertyChanged
{
public string name;
public string desc;
public int pr;
public string TaskName
{
get { return name; }
set
{
name = value;
OnPropertyChanged("TaskName");
}
}
public string Description
{
get { return desc; }
set
{
desc = value;
OnPropertyChanged("Description");
}
}
public int Priority
{
get { return pr; }
set
{
pr = value;
OnPropertyChanged("Priority");
}
}
public Task(string name, string description, int pr)
{
this.TaskName = name;
this.Description = description;
this.Priority = pr;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string pName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(pName));
}
}
}
you didn't do any thing wrong, But check your code because the priority is overwritten in some where else the clue of that is your other binding works fine, don't forget to change your binding in all the property to be like ControlProperty="{Binding ClassProperty,UpdateSourceTrigger=PropertyChanged}"