Changing Data in CollectionView (Xamarin) - c#

I use CollectionView to show data on screen, but when I change data, UI is not changing, although I am using OnPropertyChanged. Here is the code:
Xaml
<CollectionView ItemsSource="{Binding GridData}" Margin="15">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Margin="15" Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0"
Grid.Column="0"
HorizontalTextAlignment="Start"
Text="{Binding Title}"
FontSize="Small"/>
<Label Grid.Row="0"
Grid.Column="1"
HorizontalTextAlignment="End"
Text="{Binding Data}"
TextColor="Black"
FontSize="Medium">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Reference Page} , Path=BindingContext.TapCommand}"
CommandParameter="{Binding Title}" />
</Label.GestureRecognizers>
</Label>
<BoxView Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
BackgroundColor="LightGray"
CornerRadius="2"
HorizontalOptions="FillAndExpand"
HeightRequest="1"></BoxView>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
ViewModel
private List<CollectionEntity> _gridData;
public List<CollectionEntity> GridData
{
get => _gridData;
set
{
if (_gridData != value)
{
_gridData = value;
OnPropertyChanged(nameof(GridData));
}
}
}
public ICommand TapCommand
{
get
{
return new Command<CollectionView>((commandParameters) =>
{
OpenEditing(commandParameters.ToString());
OnPropertyChanged(nameof(GridData));
});
}
}
Model (is in the same file, as is ViewModel)
public class CollectionEntity: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Title { get; set; }
public string Data { get; set; }
}
So, when I tap on the Label, UI does not react. I tried to write it according to this answer, but cannot understand, what is incorrect.
UPD: new command
public ICommand TapCommand => new Command<object>((commandParameters) =>
{
OpenEditing(commandParameters.ToString()); // changing data
});

Though you had write the code about INotifyPropertyChanged in your model but you didn't implement it on the property Title and Data . Modify the code like following
private string title;
public string Title
{
get => title;
set
{
if (title!= value)
{
title = value;
OnPropertyChanged(nameof(Title));
}
}
}
private string data;
public string Data
{
get => data;
set
{
if (data!= value)
{
data= value;
OnPropertyChanged(nameof(Data));
}
}
}
In addition, the code in TapCommand seems will not change the value of source . You could binding the whole model to the command and set the title or data in command as you want .
CommandParameter="{Binding .}"
public ICommand TapCommand
{
get
{
return new Command<CollectionView>((arg) =>
{
var model = arg as CollectionEntity;
// model.Title = "xxx";
});
}
}

Related

Why this Xamarin CollectionView doesn't update UI when changing element property?

I have a chat application that have a list of chat members which should change property Lida/BgFrame when the last message is seen. Adding new elements to this list works fine, but changing properties of existing elements do nothing.
See my code below(relevant parts only, I have omitted a lot of irrelevant code for this question):
namespace MasterDetailPageNavigation.XAML
{
public static ObservableCollection<ChatMembers> Lista { get; set; } = new ObservableCollection<ChatMembers>();
public static ReaderWriterLockSlim valuesLock = new ReaderWriterLockSlim();
void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)
{
var collectionLock = (ReaderWriterLockSlim)context;
Action enterLock = writeAccess ? new Action(collectionLock.EnterWriteLock) : new Action(collectionLock.EnterReadLock);
Action exitLock = writeAccess ? new Action(collectionLock.ExitWriteLock) : new Action(collectionLock.ExitReadLock);
enterLock();
try { accessMethod(); } finally { exitLock(); }
}
public class ChatMembers : INotifyPropertyChanged // THIS IS THE BINDABLE OBJECT
{
public Color WhiteAlpha = new Color(255, 255, 255, 0.7);
public Color GreenAlpha = new Color(160.0f / 255.0f, 255.0f / 255.0f, 160.0f / 255.0f, 0.2);
private Guid _talk { get; set; }
public Guid talk
{
get { return _talk; }
set
{
_talk = value;
NotifyPropertyChanged("talk");
}
}
private string _Username { get; set; }
public string Username
{
get { return _Username; }
set
{
_Username = value;
this.NotifyPropertyChanged("Username");
}
}
private bool _Lida { get; set; }
public bool Lida
{
get { return _Lida; }
set
{
_Lida = Convert.ToBoolean(value);
FrameBg = !_Lida ? (UserFrom == MainPage.MeuID ? WhiteAlpha : GreenAlpha) : WhiteAlpha;
this.NotifyPropertyChanged("Lida");
}
}
private Color _FrameBg { get; set; }
public Color FrameBg
{
get { return _FrameBg; }
set
{
_FrameBg = value;
this.NotifyPropertyChanged("FrameBg");
}
}
private string _Msg { get; set; }
public string Msg
{
get { return _Msg; }
set
{
_Msg = value;
this.NotifyPropertyChanged("Msg");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
// Code that updates property "Lida":
tasks.Add(Task.Run(() =>
{
Color GreenAlpha = new Color(160.0f / 255.0f, 255.0f / 255.0f, 160.0f / 255.0f, 0.2);
bool itemExists = false;
valuesLock.EnterReadLock();
try
{
itemExists = Lista.Any(i => i.talk == guid);
}
finally { valuesLock.ExitReadLock(); }
if (itemExists)
{
ChatMembers item;
int indexof;
valuesLock.EnterReadLock();
try
{
item = Lista.Single(i => i.talk == guid);
indexof = Lista.IndexOf(item);
}
finally { valuesLock.ExitReadLock(); }
_ = Device.InvokeOnMainThreadAsync(() =>
{
valuesLock.EnterWriteLock();
try
{
Lista[indexof].Lida = true;
Lista[indexof].FrameBg = new Color(255, 255, 255, 0.7); //desperate try but no success
}
finally { valuesLock.ExitWriteLock(); }
});
}
}));
}
VIEW CODE BEHIND(relevant part):
CollectionViewMembers.BindingContext = MainPage.Lista;
VIEW XAML(relevant part):
<CollectionView x:Name="CollectionViewMembers" SelectionMode="None"
HorizontalScrollBarVisibility="Never" ItemsSource="{Binding .}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="6,10,5,1" Margin="0,0,0,0" BackgroundColor="{Binding FrameBg}">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Evento}" />
</StackLayout.GestureRecognizers>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
</Grid.RowDefinitions>
<Frame IsClippedToBounds="True" Grid.Row="0" Grid.Column="0" CornerRadius="30" Padding="0" Margin="0">
<ffimageloading:CachedImage DownsampleToViewSize="True" HeightRequest="100" IsOpaque="True" Source="{Binding Imagem}" Aspect="AspectFill" LoadingPlaceholder="resource://MasterDetailPageNavigation.shared.icon_default.png" ErrorPlaceholder="resource://MasterDetailPageNavigation.shared.icon_default.png" />
</Frame>
<Grid Grid.Row="0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="20" />
<RowDefinition Height="15" />
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Text="" TextColor="{Binding ColorOnline}" HorizontalOptions="Center" VerticalOptions="Center" FontSize="15">
<Label.FontFamily>
<OnPlatform x:TypeArguments="x:String" Android="Font-Awesome-Free-Solid.otf#FontAwesome5Free-Solid" iOS="FontAwesome5Free-Solid" />
</Label.FontFamily>
</Label>
<Label Grid.Column="1" Grid.Row="0" Text="{Binding Username}" HorizontalOptions="StartAndExpand" VerticalOptions="Center" FontSize="18" FontAttributes="Bold" LineBreakMode="TailTruncation" />
<Label Grid.ColumnSpan="2" Grid.Column="0" Grid.Row="1" Text="{Binding Msg}" FontSize="15" HorizontalOptions="StartAndExpand" VerticalOptions="Start" LineBreakMode="TailTruncation" />
<Label Grid.ColumnSpan="1" Grid.Column="1" Grid.Row="2" Text="{Binding DataHora}" FontSize="11" TextColor="#CACACA" HorizontalOptions="End" VerticalOptions="Start" />
</Grid>
</Grid>
<BoxView Margin="0,10,0,0" HeightRequest="0.05" WidthRequest="100" BackgroundColor="#CCE5E5E5"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I also tried to do Lista[indexOf]=item; but no UI update.
Where is my mistake?

Xamarin Binding property not found

I have collection view defined like this:
<RefreshView
Command="{Binding RefreshCommand}"
IsRefreshing="{Binding IsRefreshing}">
<CollectionView
ItemsSource="{Binding Menus}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="128" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
Source="{Binding ImageURI}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Aspect="AspectFill"
Opacity="0.64"
Grid.RowSpan="2"
Grid.ColumnSpan="3"/>
<Label
Text="{Binding Name}"
FontAttributes="Bold"
FontSize="Large"
TextColor="Black"
Grid.Row="0"
Grid.ColumnSpan="3"/>
<Label
Text="{Binding Description}"
FontSize="Body"
TextColor="Black"
Grid.Row="1"
Grid.ColumnSpan="3"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
But when built, nothing comes up in the view and the console show the following errros:
[0:] Binding: 'ImageURI' property not found on '<model>', target property: 'Xamarin.Forms.Image.Source'
[0:] Binding: 'Name' property not found on '<model>', target property: 'Xamarin.Forms.Label.Text'
[0:] Binding: 'Description' property not found on '<model>', target property: 'Xamarin.Forms.Label.Text'
When I change the view to this:
<Label Text="{Binding Menus.Count}"
TextColor="Black"/>
<Label Text="{Binding Menus[0]}"
TextColor="Black"/>
<Label Text="{Binding Menus[0].Name}"
TextColor="Black"/>
The result looks like this, but still produces the same error message.
[0:] Binding: 'Name' property not found on 'hollywood.Models.MenuHandle', target property: 'Xamarin.Forms.Label.Text'
The view model and model respectively look like this:
public class MenuListViewModel : BaseViewModel
{
public MenuListViewModel()
{
//RefreshMenus();
Menus.Add(new MenuHandle { Name = "Test", Description="test2"});
Title = "Menu";
}
public async Task RefreshMenus()
{
IsRefreshing = true;
TimeSpan age = DateTime.Now - MenusAge;
if (age.TotalSeconds > 1)
{
try
{
Menus = await App.ApiConnection.GetMenusAsync();
MenusAge = DateTime.Now;
}
catch { }
}
IsRefreshing = false;
}
ObservableCollection<MenuHandle> menus = new ObservableCollection<MenuHandle>();
public ObservableCollection<MenuHandle> Menus
{
get { return menus; }
private set { SetProperty(ref menus, value); }
}
bool isRefreshing;
public bool IsRefreshing
{
get { return isRefreshing; }
private set { SetProperty(ref isRefreshing, value); }
}
DateTime MenusAge = DateTime.MinValue;
public ICommand RefreshCommand => new Command(async () => await RefreshMenus());
}
}
Model class:
public class MenuHandle
{
[JsonProperty("name")]
public string Name;
[JsonProperty("url_name")]
public string URLName;
[JsonProperty("description")]
public string Description;
[JsonProperty("image")]
public Uri ImageURI;
}
Any suggestions would be greatly appreciated.
You need to declare your variables as properties because bindings works with properties:
public class MenuHandle
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url_name")]
public string URLName { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("image")]
public Uri ImageURI { get; set; }
}

How to Create a Method to save user input

I have the following view here where the user is able to input three different items: LockedOutBy, LockedOutFor, and LockedOutDate. I am having trouble creating a method within my view-model to allow the user to input these three items and once they hit 'ok', it will save the three items. Sorry if it is vague or if there is not enough info, please let me know if there is anything else needed.
Thank you.
View
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="1" Text="Locked Out By:"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding LockedOutBy, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="2" Text="Locked Out For:"/>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding LockedOutFor, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="3" Text="Locked Out Date:"/>
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding LockedOutDate, UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{Binding Path=OKCommand}"
View-Model
This is what I have so far in my view-model.
private string _LockedOutFor;
public string LockedOutFor
{
get { return _LockedOutFor; }
set
{
_LockedOutFor = value;
OnPropertyChanged("OwnerName");
}
}
private string _LockedOutBy;
public string LockedOutBy
{
get { return _LockedOutBy; }
set
{
_LockedOutBy = value;
OnPropertyChanged("Street");
}
}
private int _LockedOutDate;
public int LockedOutDate
{
get { return _LockedOutDate; }
set
{
_LockedOutDate = value;
OnPropertyChanged("StreetOverflow");
}
}
public ICommand CancelCommand
{
get { return new RelayCommand(c => OnCancelLock()); }
}
public ICommand OKCommand
{
get { return new RelayCommand(c => OnOKLock()); }
}
protected void OnOKLock()
{
OnOK(LockedOutFor, LockedOutBy, LockedOutDate);
}
protected void OnCancelLock()
{
OnCancel();
}
ANotifyPropertyChanged - base implementation for all of our view models
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace SO60269403
{
public abstract class ANotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void SetProperty<T>(ref T backingStore, T newValue, [CallerMemberName] string propertyName = "")
{
var areEqual = ReferenceEquals(backingStore, newValue);
if (areEqual)
return;
backingStore = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
LockedOutViewModel - our view model
namespace SO60269403
{
public class LockedOutViewModel : ANotifyPropertyChanged
{
private string lockedOutBy;
private string lockedOutFor;
private int lockedOutDate;
public string LockedOutBy { get => lockedOutBy; set => SetProperty(ref lockedOutBy, value); }
public string LockedOutFor { get => lockedOutFor; set => SetProperty(ref lockedOutFor, value); }
public int LockedOutDate { get => lockedOutDate; set => SetProperty(ref lockedOutDate, value); }
}
}
ACommand - base implementation for all of our commands
using System;
using System.Windows.Input;
namespace SO60269403
{
public abstract class ACommand : ICommand
{
public event EventHandler CanExecuteChanged;
public virtual bool CanExecute(object parameter) => true;
public abstract void Execute(object parameter);
}
}
SaveCommand - our save command
namespace SO60269403
{
public class SaveCommand : ACommand
{
public LockedOutViewModel LockedOutViewModel { get; set; }
public override void Execute(object parameter)
{
//TODO
}
}
}
MainWindow - a view to data-bind our view model and hook up our save command
<Window
x:Class="SO60269403.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SO60269403">
<Window.Resources>
<local:LockedOutViewModel
x:Key="LockedOutViewModel" />
</Window.Resources>
<Grid
DataContext="{StaticResource LockedOutViewModel}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0">Locked Out By</TextBlock>
<TextBlock
Grid.Row="1"
Grid.Column="0">Locked Out For</TextBlock>
<TextBlock
Grid.Row="2"
Grid.Column="0">Locked Out Date</TextBlock>
<TextBox
Grid.Row="0"
Grid.Column="1"
Text="{Binding LockedOutBy}" />
<TextBox
Grid.Row="1"
Grid.Column="1"
Text="{Binding LockedOutFor}" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Text="{Binding LockedOutDate}" />
<StackPanel
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Orientation="Horizontal"
HorizontalAlignment="Right">
<StackPanel.Resources>
<Style
TargetType="Button">
<Setter
Property="Width"
Value="100" />
</Style>
</StackPanel.Resources>
<Button>Cancel</Button>
<Button>
<Button.Command>
<local:SaveCommand
LockedOutViewModel="{StaticResource LockedOutViewModel}" />
</Button.Command>
OK
</Button>
</StackPanel>
</Grid>
</Window>
if we put a breakpoint in SaveCommand.Execute then run the app, fill in some values and click the OK button, the debugger should break on our breakpoint and we can observe that the view model's properties match what we entered. this demonstrates that the data is in place ready to be saved.

Having problems binding my datagrid to data from my database

i've a problem to use datagrid in wpf mvvm project
Here is my xaml :
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
x:Class="noteManager.MainWindow"
xmlns:vm="clr-namespace:noteManager.ViewModel"
DataContext="{StaticResource noteManagerViewModel}"
Title="NoteManager" Height="490" Width="525">
<Grid Margin="0,0,0,-132.5">
<Grid.RowDefinitions>
<RowDefinition Height="10"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="200"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="110"></RowDefinition>
<RowDefinition Height="111"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Login :" FontSize="16" Grid.Column="2" Margin="51,9,50,0" Grid.RowSpan="2" Height="23" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
<TextBox Text="{Binding Login}" Grid.Row="1" Grid.Column="3" Margin="14,0,86,29" Grid.ColumnSpan="2"/>
<Button Background="LightGreen" Foreground="Green" Command="{Binding testConnexion}" x:Name="testConnexion" Content="Connexion" Grid.Row="1" Grid.Column="2" Margin="51,29,86,0" Grid.ColumnSpan="3"/>
<Button Command="{Binding addUser}" Content="+" Grid.Row="1" Grid.Column="4" Margin="34,1,20,0" RenderTransformOrigin="0.742,0.468"/>
<DataGrid Name="dataGrid1" Grid.Row="2" Margin="8,7,-22,7" AutoGenerateColumns="False"
ItemsSource="{Binding _DataGridNotes}" SelectedItem="{Binding Path=MySelectedNote}" HorizontalAlignment="Center"
Width="480" Grid.ColumnSpan="6" Grid.Column="1">
<DataGrid.Columns>
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteTitle}" Header="Titre" />
<DataGridTextColumn Width="200" Binding="{Binding Path=NoteContent}" Header="Note" />
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteCreatedAt}" Header="Date de création" />
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteUpdatedAt}" Header="Dat MAJ" />
</DataGrid.Columns>
</DataGrid>
<TextBlock Text="Titre" FontSize="16" Grid.Row="3" Grid.Column="1" Margin="27,8,7,1"/>
<TextBox Text="{Binding Path=titre, Mode=TwoWay}" Grid.Row="3" Grid.Column="2" Margin="17,10,23,10" Grid.ColumnSpan="5"/>
<TextBlock Text="Note" FontSize="16" Grid.Row="4" Grid.Column="1" Margin="27,4,7,0"/>
<TextBox Text="{Binding Path=description, Mode=TwoWay}" Grid.Row="4" Grid.Column="2" Margin="17,10,23,8" Grid.ColumnSpan="5"/>
<Button Command="{Binding Path=DeleteNote}" Background="LightPink" Foreground="red" Content="Supprimer" Grid.Row="5" Grid.Column="1" Margin="55,7,26,81" Grid.ColumnSpan="2"/>
<Button Command="{Binding Path=UpdateANote}" Content="Mettre à jour" Grid.Row="5" Grid.Column="3" Margin="14,7,67,81" Grid.ColumnSpan="2" RenderTransformOrigin="0.5,0.5"/>
<Button Command="{Binding Path=AddNote}" Content="Ajouter" Grid.Row="5" Grid.Column="4" Margin="78,7,10,81" Grid.ColumnSpan="3"/>
</Grid>
</Window>
Here is my viewModel :
namespace noteManager.ViewModel
{
public class noteManagerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
string login;
int currentUser;
public string Login
{
get
{
return login;
}
set
{
login = value; Notify("Login");
}
}
private bool _canExecute;
public noteManagerViewModel()
{
_canExecute = true;
}
private ICommand _testConnexion;
public ICommand testConnexion
{
get
{
return _testConnexion ?? (_testConnexion = new CommandHandler(() => Connexion(), _canExecute));
}
}
private ICommand _addUser;
public ICommand addUser
{
get
{
return _addUser ?? (_addUser = new CommandHandler(() => AjoutUser(), _canExecute));
}
}
private ObservableCollection<DataGridNotes> _DataGridNotes = new ObservableCollection<DataGridNotes>();
public ObservableCollection<DataGridNotes> dataGridNotes
{
// No need for a public setter
get { return _DataGridNotes; }
}
}
the other class that i use :
public class User
{
/*public User()
{
this.Note = new HashSet<Note>();
}*/
public int Id { get; set; }
public string Login { get; set; }
//public virtual ICollection<Note> Note { get; set; }
}
public class Note : INotifyPropertyChanged
{
public int Id { get; set; }
public string NoteText { get; set; }
public string ContentText { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public int UserId { get; set; }
//public virtual User User { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void Notify(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class DataGridNotes
{
private string _noteTitle;
private string _noteContent;
private string _noteCreatedAt;
private string _noteUpdatedAt;
public string NoteTitle { get { return _noteTitle; } set { _noteTitle = value; } }
public string NoteContent { get { return _noteContent; } set { _noteContent = value; } }
public string NoteCreatedAt { get { return _noteCreatedAt; } set { _noteCreatedAt = value; } }
public string NoteUpdatedAt { get { return _noteUpdatedAt; } set { _noteUpdatedAt = value; } }
}
sorry for the ugly code, new to c# for a project.
i want to use the datagrid in my viewmodel but don't find a way to make it work (would like to write data from mysql database in the datagrid
Have you an idea to make it work ?
thx in advance
Ok, It's tough to spot what you are doing wrong without seeing the ViewModel, however you may want to check the following:
1) The DataContext is correct.
2) The property _DataGridNotes exists. Check the program output to make sure that there are no warnings informing you that bindings are broken.
The property you are looking to have should look something like this:
List<Note> _DataGridNotes
{
get
{
// get notes from SQL request
// construct list of Note and return list
}
}
You should also make sure that the Note class contains the properties required (NoteTitle, NoteContent, NoteCreatedAt, NoteUpdatedAt).
It might also be worth passing back some dummy notes to debug if the problem lies in the request to the SQL database.
The problem is that you are trying to bind to a private Observable collection _DataGridNotes where you should be binding to the property dataGridNotes:
ItemsSource="{Binding dataGridNotes}"

Failing To Bind Dynamically Generated Buttons & Textboxes in WPF

I was working on dynamic generation of labels, buttons and Textbox in my WPF application. Well I was successful in dynamically creating them but I am facing one major issue in it.
Xaml:
<ListBox x:Name="myViewChannelList" HorizontalAlignment="Stretch" Height="Auto" ItemsSource="{Binding}" Margin="0" VerticalAlignment="Stretch" Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="170" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Path=ChanelName}" Margin="50,20,0,0"></Label>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Path=VoltageText}" Height="25" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
<Button Grid.Column="1" Content="Set" Height="25" Command="{Binding ElementName=myViewChannelList, Path=DataContext.SetCommand}" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" ></Button>
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Model Class:
private string _ChanelName = "";
public String ChanelName
{
get
{
return _ChanelName;
}
set
{
if (value != _ChanelName)
{
_ChanelName = value;
OnPropertyChanged("ChanelName");
}
}
}
// Constructor
public VoltageModel(string ChanelName)
{
this.ChanelName = ChanelName;
}
public override string ToString()
{
return _ChanelName;
}
ViewModel Class:
class ChannelList : ObservableCollection<VoltageModel>, INotifyPropertyChanged
{
private string _VoltageText;
public string VoltageText
{
get { return _VoltageText; }
set
{
_VoltageText = value;
OnPropertyChanged("VoltageText");
}
}
// Method gets called when Set Button Is Clicked
public void SetCommandExecuted()
{
string val = VoltageText;
}
//Notify Property Changed members are present
}
Xaml.cs Class:
ChannelList myChanels = new ChannelList();
public VoltageView() // Constructor
{
InitializeComponent();
myChanels.Add(new VoltageModel("VDD__Main"));
myChanels.Add(new VoltageModel("VDD__IO__AUD"));
myChanels.Add(new VoltageModel("VDD__CODEC__AUD"));
myViewChannelList.DataContext = myChanels;
}
This gives me 3 Labels(Content as above), 3 textboxes and 3 buttons when I run the application.
Now when I enter the value inside the textbox it shows null on button click when I put a breakpoint in SetCommandExecuted(). Most importantly any of the 4 button I click generates the event. I want the first textbox and first button to be in sync(bind), 2nd textbx and 2nd button to be in sync and so on. Basically each control must be in sync with the other control in a row. It should not effect the other rows. Is it possible???
Here is the solution to your question. As general practice you want to avoid all logic, building your data, etc. in the code behind. All the business logic should be in the view model which will make it easier to unit test.
Here is the view
.xaml
<StackPanel>
<ListBox HorizontalAlignment="Stretch"
Height="Auto"
ItemsSource="{Binding VoltageCollection}"
VerticalAlignment="Stretch"
Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Width="100"
Content="{Binding ChannelName}" />
<TextBox Width="100"
Text="{Binding VoltageText}" />
<Button Margin="10,0,0,0"
Content="Set"
Command="{Binding VoltageCommand}"
CommandParameter="{Binding VoltageText}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Here is the code behind
.xaml.cs
private ChannelListViewModel m_voltageViewModel;
public MainWindow()
{
InitializeComponent();
m_voltageViewModel = new ChannelListViewModel();
m_voltageViewModel.Initialize();
DataContext = m_voltageViewModel;
}
Here is the Model: VoltageModel
public class VoltageModel : INotifyPropertyChanged
{
public string ChannelName { get; set; }
private string m_voltageText;
public string VoltageText
{
get { return m_voltageText; }
set
{
m_voltageText = value;
OnPropertyChanged("VoltageText");
}
}
public ICommand VoltageCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the ViewModel: ChannelListViewModel
public class ChannelListViewModel
{
private ICommand m_voltageCommand;
public ChannelListViewModel()
{
m_voltageCommand = new DelegateCommand(x => SetCommandExecute(x));
}
public void Initialize()
{
VoltageCollection = new ObservableCollection<VoltageModel> { new VoltageModel() { ChannelName = "VDD__Main", VoltageText = String.Empty, VoltageCommand = m_voltageCommand },
new VoltageModel() { ChannelName = "VDD__IO__AUD", VoltageText = String.Empty, VoltageCommand = m_voltageCommand },
new VoltageModel() { ChannelName = "VDD__CODEC__AUD", VoltageText = String.Empty, VoltageCommand = m_voltageCommand }};
}
public ObservableCollection<VoltageModel> VoltageCollection { get; set; }
public void SetCommandExecute(object voltageText)
{
Debug.WriteLine(voltageText);
}
}
Finally simple DelegateCommand class DelegateCommand
public class DelegateCommand : ICommand
{
Action<object> m_executeDelegate;
public DelegateCommand(Action<object> executeDelegate)
{
m_executeDelegate = executeDelegate;
}
public void Execute(object parameter)
{
m_executeDelegate(parameter);
}
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
}
i didn't get much into what was wrong since i recognized 2 things that were generally very wrong ,and they might be the problem for unexpected behavior on your part .
the first : your DataTemplate places your control one on top of the other .
fix :
<DataTemplate >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Path=ChanelName}" />
<TextBox Grid.Column="1" Text="{Binding Path=VoltageText}" />
<Button Grid.Column="2" Command="{Binding ElementName=myViewChannelList, Path=DataContext.SetCommand}" />
</Grid>
</DataTemplate>
the second : your Properties are set after PropertyChanged event was risen so they would not be updated until the next time you input a value.
fix :
private T _property;
public T Property
{
get { return _property; }
set
{
_property = value;
OnPropertyChanged("Property");
}
}
make these fixes and edit your post if you still have issues post a comment under my answer.

Categories