I have a problem in adding and deleting items of ObservableCollection. After clicking on binded buttons, SQL Server database successfully updates, but after calling methods WorkerInfoCollection.Add and WorkerInfoCollection.Remove UI isn't updating. Moreover, WorkerInfoCollection.Remove(SelectedWorker) doesn't remove item at all. I can remove this item only if I use LINQ First or Default, but still - there are no changes in UI.
Through many hours of debugging and web searching, I've tried to change buttons Command Parameters: ElementName and Path, using RelativeSource:AncestorType, setting Command value in ViewModel constructor, setting Command value in setter, raise OnPropertyChanged in ObservableCollection, used different modes of Binding, adding and removing items from ObservableCollection, even tried to reinitialize ObservableCollection and fill it directly from database at runtime (kinda stupid). Nothing helped. Searching in web also didn't help the situation.
MainWindow.xaml
<Grid>
<DataGrid Name="WorkerInfoData"
HorizontalAlignment="Stretch"
Margin="10,10,50,10"
VerticalAlignment="Stretch"
ItemsSource="{Binding WorkerInfoCollection}"
SelectedItem="{Binding SelectedWorker,
Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID робітника"
Binding="{Binding WorkerId}" />
<DataGridTemplateColumn Header="Фото"
Width="SizeToCells"
IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding WorkerPhoto}"
Height="75"
Width="75"
Stretch="UniformToFill"
RenderOptions.BitmapScalingMode="Fant"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="ПІБ"
Binding="{Binding WorkerFullName}" />
<...>
<DataGridTextColumn Header="Хоббі"
Binding="{Binding WorkerHobby}" />
</DataGrid.Columns>
</DataGrid>
<StackPanel Panel.ZIndex="3"
Name="addRecordPanel"
Orientation="Horizontal"
HorizontalAlignment="Right"
Margin="0,0,-369,0"
Width="417"
DataContext="{Binding NewWorker,
Mode=TwoWay}">
<Grid>
<Button Name="buttonHideAddRecordPanel"
Width="48"
Height="48"
Click="ButtonHideAddRecordPanel_Click"
Visibility="Hidden"
Margin="0 0 0 100">
<Button.Background>
<SolidColorBrush Color="White"/>
</Button.Background>
<Image Source="Images/ClosePanel.png"
RenderOptions.BitmapScalingMode="Fant"/>
</Button>
<Button Name="buttonShowAddRecordPanel"
Height="48"
Width="48"
Click="ButtonShowAddRecordPanel_Click"
Margin="0 0 0 100">
<Button.Background>
<SolidColorBrush Color="White"/>
</Button.Background>
<Image Source="Images/AddRecord.ico"
RenderOptions.BitmapScalingMode="Fant"/>
</Button>
</Grid>
<Border BorderBrush="DarkGray"
BorderThickness="1.5"
Width="369"
Background="WhiteSmoke"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="0 0 0 10"
SnapsToDevicePixels="True">
<TextBlock Text="Дані нового робітника"
FontSize="16"
Background="Gainsboro"
TextAlignment="Center"
Margin="0"
Padding="0 0 0 3"/>
<TextBlock Text="ID робітника"
Margin="0 10 0 0"/>
<TextBox Text="{Binding WorkerId,
UpdateSourceTrigger=PropertyChanged}"
Margin="0 5"
Width="300"
Background="White"/>
<TextBlock Text="Фото робітника"
Margin="0 5 0 0"/>
<Border BorderBrush="DarkGray"
BorderThickness="1"
Width="300"
Margin="0 5 0 0">
<Image Source="{Binding WorkerPhoto,
NotifyOnTargetUpdated=True,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay,
Converter=
{conv:ByteToImage}}"
Height="300"
Stretch="UniformToFill"/>
</Border>
<Button Name="AddNewWorkerPhoto"
Width="150"
FontSize="14"
Content="Завантажити фото"
Command="{Binding AddRecordImage}"
CommandParameter="{Binding
ElementName=addRecordPanel,
Path=DataContext}">
<Button.DataContext>
<VM:WorkerInfoViewModel/>
</Button.DataContext>
</Button>
<...>
<Button Name="AddRecord"
Content="Додати запис"
FontSize="14"
Width="150"
Margin="0 10"
Command="{Binding AddRecord}"
CommandParameter="{Binding
ElementName=addRecordPanel,
Path=DataContext}">
<Button.DataContext>
<VM:WorkerInfoViewModel/>
</Button.DataContext>
</Button>
</StackPanel>
</ScrollViewer>
</Border>
</StackPanel>
<StackPanel Panel.ZIndex="3"
Name="deleteRecordPanel"
Orientation="Horizontal"
HorizontalAlignment="Right"
Margin="0,0,-369,0"
Width="417">
<Grid>
<Button Name="DeleteRecord"
Height="48"
Width="48"
Margin="0 100 0 0"
Command="{Binding DeleteRecord}"
CommandParameter="{Binding
ElementName=WorkerInfoData,
Path=SelectedItem}">
<Button.DataContext>
<VM:WorkerInfoViewModel/>
</Button.DataContext>
<Button.Background>
<SolidColorBrush Color="White"/>
</Button.Background>
<Image Source="Images/DeleteRecord.png"
RenderOptions.BitmapScalingMode="Fant"/>
</Button>
</Grid>
</StackPanel>
</Grid>
WorkerInfo.cs
public class WorkerInfo : INotifyPropertyChanged
{
private int workerId;
<...>
public int WorkerId
{
get { return workerId; }
set
{
workerId = value;
OnPropertyChanged(nameof(WorkerId));
}
<...>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = "")
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(prop));
}
RelayCommand.cs
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool>
canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
}
ViewModelBase.cs
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = "")
{
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(prop));
}
}
WorkerInfoViewModel.cs
public class WorkerInfoViewModel : ViewModelBase
{
private SqlDataAdapter dataAdapter;
private DataTable dataTable;
private WorkerInfo newWorker;
private WorkerInfo selectedWorker;
public WorkerInfo NewWorker
{
get
{
if (newWorker == null)
{
return newWorker = new WorkerInfo();
}
else return newWorker;
}
set
{
newWorker = value;
}
}
public WorkerInfo SelectedWorker
{
get { return selectedWorker; }
set
{
selectedWorker = value;
OnPropertyChanged("SelectedWorker");
}
}
private ObservableCollection<WorkerInfo> workerInfoCollection;
public ObservableCollection<WorkerInfo> WorkerInfoCollection
{
get { return workerInfoCollection; }
set
{
workerInfoCollection = value;
OnPropertyChanged("WorkerInfoCollection");
}
}
public ICommand AddRecord { get; }
public ICommand AddRecordImage { get; }
public ICommand UpdateRecord { get; }
public ICommand UpdateRecordImage { get; }
private ICommand deleteCommand;
public ICommand DeleteRecord
{
get
{
if (deleteCommand == null)
{
deleteCommand = new RelayCommand(parameter =>
DeleteRecord_Click(parameter));
}
return deleteCommand;
}
}
public WorkerInfoViewModel()
{
string selectQuery = "SELECT * FROM [WorkerInfo]";
using (SqlConnection connection = new
SqlConnection(builder.ConnectionString))
{
using (SqlCommand command = new SqlCommand(selectQuery,
connection))
{
connection.Open();
using (dataAdapter = new SqlDataAdapter(command))
{
dataTable = new DataTable();
dataAdapter.Fill(dataTable);
WorkerInfoCollection = new
ObservableCollection<WorkerInfo>();
foreach (DataRow dataRow in dataTable.Rows)
{
WorkerInfoCollection.Add
(
new WorkerInfo()
{
WorkerId =
Convert.ToInt32(dataRow["WorkerId"]),
<...>
WorkerHobby =
dataRow["WorkerHobby"].ToString()
}
);
}
}
connection.Close();
}
}
AddRecord = new RelayCommand(parameter =>
AddRecord_Click(parameter));
AddRecordImage = new RelayCommand(parameter =>
AddWorkerPhoto_Click(parameter));
UpdateRecord = new RelayCommand(parameter =>
UpdateRecord_Click(parameter));
UpdateRecordImage = new RelayCommand(parameter =>
UpdateWorkerPhoto_Click(parameter));
//DeleteRecord = new RelayCommand(parameter =>
DeleteRecord_Click(parameter));
}
private void AddRecord_Click(object parameter)
{
NewWorker = (WorkerInfo)parameter;
WorkerInfoCollection.Add
(
new WorkerInfo()
{
WorkerId = NewWorker.WorkerId,
<...>
WorkerHobby = NewWorker.WorkerHobby
}
);
string insertQuery = "INSERT INTO [WorkerInfo] (" +
"WorkerId, " +
<...>
"#WorkerHobby)";
using (SqlConnection connection = new
SqlConnection(builder.ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(insertQuery,
connection))
{
try
{
command.Parameters.AddWithValue("#WorkerId",
NewWorker.WorkerId);
<...>
command.Parameters.AddWithValue("#WorkerHobby",
NewWorker.WorkerHobby);
command.ExecuteNonQuery();
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show("error");
log.Error(ex);
}
}
}
}
private void DeleteRecord_Click(object parameter)
{
SelectedWorker = (WorkerInfo)parameter;
string deleteRecord = "DELETE FROM [WorkerInfo] " +
"WHERE WorkerId = #WorkerId";
if (SelectedWorker != null)
{
using (SqlConnection connection = new
SqlConnection(builder.ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(deleteRecord,
connection))
{
try
{
command.Parameters.AddWithValue("#WorkerId",
SelectedWorker.WorkerId);
command.ExecuteNonQuery();
connection.Close();
WorkerInfoCollection.Remove(SelectedWorker);
}
catch (Exception ex)
{
MessageBox.Show("error");
log.Error(ex);
}
}
}
}
}
When you do:
<Button.DataContext>
<VM:WorkerInfoViewModel/>
</Button.DataContext>
That instantiates another instance of workerinfo viewmodel.
Remove that.
You want to be adding and removing from the same observablecollection in the same viewmodel instance the itemssource of your datagrid is bound to.
As it is, you have at least 3 instances of that viewmodel.
Make sure you just have the one instance of your viewmodel and that is the datacontext of mainwindow and hence the buttons and datagrid.
Related
I have to make a WPF App to add, delete and update Items for Client Management purposes. I have so far been able to add and remove the items to the List using MVVM but I am unable to update the selected items. I require help in this regard.
The code for the project is as follows:
Details Class.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Assignment_2.Model
{
public class Details :INotifyPropertyChanged
{
private string name;
public string Name
{ get { return name; } set { name = value; OnPropertyChanged(Name); } }
private string company;
public string Company
{ get { return company; } set { company = value; OnPropertyChanged(Company); } }
private string region;
public string Region
{ get { return region; } set { region = value; OnPropertyChanged(Region); } }
private string hra;
public string HRA
{
get { return hra; }
set
{
hra = value;
OnPropertyChanged(HRA);
}
}
private string basic;
public string Basic
{ get { return basic; }
set { basic = value;
OnPropertyChanged(Basic);
}
}
private string pf;
public string PF
{ get { return pf; }
set
{
pf = value;
OnPropertyChanged(PF);
}
}
private string salary;
public string Salary
{ get { return salary; }
set
{
salary = value;
OnPropertyChanged(Salary);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string det)
{
PropertyChangedEventHandler pc = PropertyChanged;
if (pc != null)
pc(this, new PropertyChangedEventArgs(det));
}
}
}
DetailsViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Assignment_2.Commands;
using Assignment_2.Model;
namespace Assignment_2.ViewModel
{
public class DetailsViewModel : INotifyPropertyChanged
{
private Details record;
public Details Record
{
get { return record; }
set { record = value; NotifyPropertyChanged("Record"); }
}
private ObservableCollection<Details> records;
public ObservableCollection<Details> Records
{
get { return records; }
set { records = value; } // NotifyPropertyChanged("Records");
}
private ICommand _SubmitCommand;
public ICommand SubmitCommand
{
get
{
if (_SubmitCommand == null)
{
_SubmitCommand = new RelayCommand(SubmitExecute, CanSubmitExecute, false);
}
return _SubmitCommand;
}
}
public DetailsViewModel()
{
Record = new Details();
Records = new ObservableCollection<Details>();
}
private void SubmitExecute(object parameter)
{
var TempRecord = new Details();
//TempRecord = Record;
TempRecord.Name = Record.Name;
TempRecord.Company = Record.Company;
TempRecord.Region = Record.Region;
TempRecord.HRA = Record.HRA;
TempRecord.Salary = Record.Salary;
TempRecord.Basic = Record.Basic;
TempRecord.PF = Record.PF;
Records.Add(TempRecord);
}
private bool CanSubmitExecute(object parameter)
{
if (string.IsNullOrEmpty(Record.Name) || string.IsNullOrEmpty(Record.Company))
{
return false;
}
else
{
return true;
}
}
private ICommand _SubmitCommand1;
public ICommand SubmitCommand1
{
get
{
if (_SubmitCommand1 == null)
{
_SubmitCommand1 = new RelayCommand(SubmitExecute1, CanSubmitExecute1, false);
}
return _SubmitCommand1;
}
}
public int findIndex(Details myDetails)
{
int count = 0;
for (int i = 0; i < Records.Count; i++)
{
count = 0;
Details myRecord = Records[i];
if (myRecord.Name == myDetails.Name)
count++;
else continue;
if (myRecord.Company == myDetails.Company)
count++;
else continue;
if (myRecord.Region == myDetails.Region)
count++;
else continue;
if (myRecord.Salary == myDetails.Salary)
count++;
else continue;
if (myRecord.HRA == myDetails.HRA)
count++;
else continue;
if (myRecord.Basic == myDetails.Basic)
count++;
else continue;
if (myRecord.PF == myDetails.PF)
count++;
else continue;
if (count == 7)
return i;
}
return -1;
}
private void SubmitExecute1(object parameter)
{
Records.Remove((Details)parameter);
}
private bool CanSubmitExecute1(object parameter)
{
if (string.IsNullOrEmpty(Record.Name) || string.IsNullOrEmpty(Record.Company))
{
return false;
}
else
{
return true;
}
}
private ICommand _SubmitCommand2;
public ICommand SubmitCommand2
{
get
{
if (_SubmitCommand2 == null)
{
_SubmitCommand2 = new RelayCommand(SubmitExecute2, CanSubmitExecute2, false);
}
return _SubmitCommand2;
}
}
private void SubmitExecute2(object parameter)
{
}
private bool CanSubmitExecute2(object parameter)
{
if (string.IsNullOrEmpty(Record.Name) || string.IsNullOrEmpty(Record.Company))
{
return false;
}
else
{
return true;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string de)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(de));
}
}
}
RelayCommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Assignment_2.Commands
{
class RelayCommand : ICommand
{
Action<object> executeAction;
Func<object, bool> canExecute;
bool canExecuteCache;
public RelayCommand(Action<object> executeAction, Func<object, bool> canExecute, bool canExecuteCache)
{
this.canExecute = canExecute;
this.executeAction = executeAction;
canExecuteCache = canExecuteCache;
}
public bool CanExecute(object parameter)
{
if (canExecute == null)
{
return true;
}
else
{
return canExecute(parameter);
}
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
executeAction(parameter);
}
}
}
MainWindow.xaml
<Window x:Class="Assignment_2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Assignment_2.ViewModel"
Title="MainWindow" Height="401" Width="472">
<Window.Resources>
<vm:DetailsViewModel x:Key="DetailsViewModel"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource DetailsViewModel}}" RenderTransformOrigin="0.5,0.5" Margin="0,0,1,1">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="-0.21"/>
<TranslateTransform/>
</TransformGroup>
</Grid.RenderTransform>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="20.59"/>
<RowDefinition Height="22.906"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="29*"/>
<RowDefinition Height="293*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Text=" Name" HorizontalAlignment="Center" Margin="0,0,0,4" Grid.RowSpan="2" Width="35" />
<TextBox Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.Name,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin="2,0,0,1" Grid.RowSpan="2"/>
<TextBlock Text="Company" HorizontalAlignment="Center" Margin="0,2,0,3" Width="51" Grid.RowSpan="2" Grid.Row="1"/>
<TextBox Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.Company,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="2,2,0,1" Grid.Row="1"/>
<TextBlock Grid.Row="2" Text="Region" HorizontalAlignment="Center" Margin="0,1,0,4" Width="37"/>
<TextBox Grid.Row="2" Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.Region,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="3,1,0,4"/>
<TextBlock Grid.Column="1" Text="HRA" HorizontalAlignment="Left" Margin="131,-1,0,2" Width="23"/>
<TextBox Grid.Column="1" Width="83" HorizontalAlignment="Left" Text="{Binding Record.HRA,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="162,0,0,1"/>
<TextBlock Grid.Column="1" Text="Basic" HorizontalAlignment="Left" Margin="129,2,0,13" Width="26" Grid.RowSpan="2" Grid.Row="1"/>
<TextBox Grid.Column="1" Width="81" HorizontalAlignment="Left" Text="{Binding Record.Basic,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="162,2,0,3" Grid.Row="1"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="PF" HorizontalAlignment="Left" Margin="144,1,0,5" Width="12"/>
<TextBox Grid.Row="2" Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.PF,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="162,2,0,1"/>
<TextBlock Grid.Column="1" Text="Salary" HorizontalAlignment="Left" Margin="268,-1,0,1" Width="36"/>
<TextBox Grid.Column="1" Width="100" HorizontalAlignment="Left" Text="{Binding Record.Salary,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="304,0,0,21" Grid.RowSpan="2"/>
<Button Content="Add" Command="{Binding SubmitCommand }" HorizontalAlignment="Left" Grid.Row="4" Margin="121,4,0,5" Width="44" Grid.Column="1"/>
<ListView x:Name="ListedView" ItemsSource="{Binding Records}" Width="Auto" Grid.Row="5" Margin="38,3,13,36" Grid.ColumnSpan="2">
<ListView.View >
<GridView >
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="Auto" />
<GridViewColumn Header="Company" DisplayMemberBinding="{Binding Company}" Width="Auto"/>
<GridViewColumn Header="Region" DisplayMemberBinding="{Binding Region}" Width="Auto" />
<GridViewColumn Header="HRA" DisplayMemberBinding="{Binding HRA}" Width="Auto" />
<GridViewColumn Header="Basic" DisplayMemberBinding="{Binding Basic}" Width="Auto" />
<GridViewColumn Header="PF" DisplayMemberBinding="{Binding PF}" Width="Auto" />
<GridViewColumn Header="Salary" DisplayMemberBinding="{Binding Salary}" Width="Auto" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Remove" Margin="1,36,50,150" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListView},Path=DataContext.SubmitCommand1}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Any help is appreciated in this regard. Thanks
If I understand your issue correctly, you should bind the SelectedItem property of the ListView to be able to edit the currently selected item in the TextBox elements above the ListView:
<ListView SelectedItem="{Binding Record}" ... />
With or without Binding you can do it like this :
if ((sender as ListView).SelectedValue != null)
{
string item = (sender as ListView).SelectedValue.ToString();
SelectedIndex = (sender as ListView).SelectedIndex;
list.Items[SelectedIndex] = "Updated";
}
(I like to add a ContextMenu on my ListViewItems so it is easier for users to modify the specified clicked item) :
<ListView x:Name="list">
<ListView.ContextMenu>
<ContextMenu x:Name="contextmenu">
<MenuItem x:Name="Add" Header="Add" Click="Add_Click"/>
<MenuItem x:Name="Del" Header="Delete" Click="Del_Click"/>
<MenuItem x:Name="Update" Header="Update" Click="Update_Click"/>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
so I've been working on a smart home implementation in c# for quite a while now but I can't figure out how to inform the Command which executes something, in this case just a remove button as an example, about the item of the Observable that triggers it.
To implement the Collection I'm using a template which also contains a button so that each item of the Collection has a button by it's own.
ObservableCollection with remove Button for each Item
So as you see in there, I have a Remove Button for each Item that is in the ObservableCollection and if I press it I want the Item which contains the Button to be removed but without using something like selectedItem because in that case I need to selected the item first before using the button which is not what I really want because you can press the button without selecting the item/row.
In later time the button will be exchanged for a different function which needs to know which item the button belongs to.
xaml code:
<Page x:Class="Smart_Home.switchPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Smart_Home"
mc:Ignorable="d"
Title="switchPage" Height="490" Width="998" Background="#10636e" >
<Page.Resources>
<local:SwitchViewmodel x:Key="SwitchViewmodel" />
<DataTemplate x:Key="UserTemplate">
<StackPanel>
<Grid>
<Grid Width="970" Margin="0,0,0,0">
<Grid HorizontalAlignment="Left" VerticalAlignment="Center" Width="300">
<TextBlock Text="{Binding Path=schalterName}"/>
</Grid>
<Grid HorizontalAlignment="Center" Width="380">
<Grid HorizontalAlignment="Left" VerticalAlignment="Center" Width="200">
<TextBlock Text="{Binding Path=schalterRaum}"/>
</Grid>
<Grid HorizontalAlignment="Right" VerticalAlignment="Center" Width="30">
<TextBlock Text="{Binding Path=schalterNummer}"/>
</Grid>
</Grid>
<Grid HorizontalAlignment="Right" VerticalAlignment="Center" Width="60">
<Button Content="Switch" Height="40" Command="{Binding RemoveSelectedSwitch, Source={StaticResource SwitchViewmodel}}" />
</Grid>
</Grid>
</Grid>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid x:Name="WindowSelection" HorizontalAlignment="Left" Height="490" Margin="0,0,0,0" VerticalAlignment="Top" Width="998">
<Grid Height="400" VerticalAlignment="Top" Grid.ColumnSpan="2">
<Grid x:Name="ListNameGrid" VerticalAlignment="Top" Height="30">
<Grid HorizontalAlignment="Left" Margin="10,0,0,0">
<Label Content="Name" FontSize="14" Foreground="White"/>
</Grid>
<Grid HorizontalAlignment="Center" Width="400">
<Grid HorizontalAlignment="Left">
<Label Content="Room" FontSize="14" Foreground="White"/>
</Grid>
<Grid HorizontalAlignment="Right">
<Label Content="Number" FontSize="14" Foreground="White"/>
</Grid>
</Grid>
<Grid HorizontalAlignment="Right" Margin="0,0,10,0">
<Label Content="Status" FontSize="14" Foreground="White" />
</Grid>
</Grid>
<Grid x:Name="ListGrid" VerticalAlignment="Top" Margin="0,30,0,0">
<ListBox x:Name="Switches_lb" IsSynchronizedWithCurrentItem="true" FontSize="20" Foreground="White" ItemsSource="{Binding SwitchesList, Source={StaticResource SwitchViewmodel}}" ItemTemplate="{StaticResource UserTemplate}" Background="#10636e" BorderThickness="0" Margin="5,0,5,0"/>
</Grid>
</Grid>
</Grid>
</Page>
Viewmodel:
public class SwitchViewmodel
{
public int selectedCollection { get; }
public ObservableCollection<SwitchView> SwitchesList { get; set; }
public ICommand AddNewSwitch { get; set; }
public ICommand RemoveSelectedSwitch { get; set; }
public SwitchViewmodel()
{
AddNewSwitch = new Command(AddNewSwitchExecuteMethod, AddNewSwitchcanexecuteMethod);
RemoveSelectedSwitch = new Command(RemoveSelectedSwitchExeCuteMethod, RemoveSelectedSwitchAddNewSwitchcanexecuteMethod);
SwitchesList = new ObservableCollection<SwitchView>
{
new SwitchView() { schalterName = "Wandlampe", schalterRaum = "Wohnzimmer", schalterNummer = 1, schalterStatus = false },
new SwitchView() { schalterName = "Deckenlampe", schalterRaum = "Küche", schalterNummer = 2, schalterStatus = false },
new SwitchView() { schalterName = "Tischlampe", schalterRaum = "Wohnzimmer", schalterNummer = 39, schalterStatus = false },
};
}
private bool RemoveSelectedSwitchAddNewSwitchcanexecuteMethod(object parameter)
{
return true;
}
private void RemoveSelectedSwitchExeCuteMethod(object parameter)
{
//In here remove method
}
private bool AddNewSwitchcanexecuteMethod(object parameter)
{
return true;
}
private void AddNewSwitchExecuteMethod(object parameter)
{
Console.WriteLine("Command test");
Console.WriteLine("Count vorher: " + SwitchesList.Count());
SwitchesList.Add(new SwitchView() { schalterName = "Wandlampe", schalterRaum = "Wohnzimmer", schalterNummer = 1, schalterStatus = false });
Console.WriteLine("Count nachher: " + SwitchesList.Count());
}
}
Collection:
public class SwitchView
{
public string schalterName { get; set; }
public string schalterRaum { get; set; }
public int schalterNummer { get; set; }
public Boolean schalterStatus { get; set; }
}
Command:
public class Command : ICommand
{
Action<object> executeMethod;
Func<object, bool> canexecuteMethod;
public Command(Action<object> executeMethod, Func<object, bool> canexecuteMethod)
{
this.executeMethod = executeMethod;
this.canexecuteMethod = canexecuteMethod;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
executeMethod(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
So it would be really helpful if someone could help me out with my problem as I'm really new in using MVVM pattern in c# wpf.
Thank you and have a nice day
add CommandParameter.
<Button Content="Switch" Height="40"
Command="{Binding RemoveSelectedSwitch, Source={StaticResource SwitchViewmodel}}"
CommandParameter="{Binding}" />
and use received parameter in command method.
private void RemoveSelectedSwitchExeCuteMethod(object parameter)
{
var item = parameter as SwitchView;
}
since Button is inside ListBox ItemTemplate, CommandParameter="{Binding}" will return DataContext of ListBoxItem - which means corresponding data item
Im working on a small wpf project to understand c# and mvvm better.
Im currently having a problem, that I dont know how to pass data between two pages.
What I want to do:
I have two pages, they are the content of my MainWindow.
In PageViewCustomers I have a DatagridView(there are acutally two, but I just need one for this task), when I double click on a row, I open PageAddStaticIPAddress. Now I want to be able to access the row I double clicked in PageViewCustomers in the PageAddStaticIPAddress. I thought about passing the selected row as a CommandParameter in the xaml of PageViewCustomers.
But now I dont know how to access it from PageAddStaticIPAddress..
I have seen some solutions, but they all solve it by having Code in the View. I try to have as less code in the view as possible and solve it by only using Bindings,commands and my viewmodels.
Here is my current code:
PageViewCustomers.xaml
<Page x:Class="StaticIPConfiger.PageViewCustomers"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:StaticIPConfiger"
xmlns:localvm="clr-namespace:StaticIPConfiger.Modelle"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500"
Title="Kundenübersicht">
<Page.DataContext>
<localvm:VModel />
</Page.DataContext>
<Grid DataContext="{Binding}" >
<DataGrid IsReadOnly="True" Name="dgCustomers" ItemsSource="{Binding AlleKunden}" AutoGenerateColumns="False" Margin="0,0,0,197">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=c_name}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="Standort" Binding="{Binding Path=l_name}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding UpdateLocations}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
<DataGrid IsReadOnly="True" Name="dg2Customers" ItemsSource="{Binding AlleStandorte}" AutoGenerateColumns="False" Margin="0,168,0,10" >
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding OpenAddNewIPAddress}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=SelectedItem}"/>
</DataGrid.InputBindings>
<DataGrid.Columns>
<DataGridTextColumn Header="StandortId" Binding="{Binding Path=l_id}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="Standort" Binding="{Binding Path=l_name}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Class VModel.cs (VModel of PageViewCustomers)
namespace StaticIPConfiger.Modelle {
public class VModel : INotifyPropertyChanged {
DataView _alleKunden;
public VModel() {
DataTable dt = new DataTable();
using (SqlConnection connection = new SqlConnection("Data Source=.\\WERBASWEB;Initial Catalog=customer;Persist Security Info=True;User ID=sa;Password=Dotnet123!")) {
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = new SqlCommand("Select c_id,c_name, l_id,l_name, a_town, a_pcode, a_street from dbo.[AllCustomers]", connection);
adapter.Fill(dt);
}
AlleKunden = dt.DefaultView;
AlleStandorte = null;
}
public DataView AlleKunden { get; private set; }
public DataView AlleStandorte { get { return _alleKunden; } private set { _alleKunden = value;
RaisePropertyChanged("AlleStandorte");
} }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Methods
private void RaisePropertyChanged(string propertyName) {
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Commands
void UpdateLocationView(object parameter) {
DataRowView selectedRow = (DataRowView)parameter;
string customerid = selectedRow.Row[0].ToString();
string locationid = selectedRow.Row[2].ToString();
DataTable dt = SelectStatements.SelectLocationsForCustomer(locationid,customerid);
AlleStandorte = dt.DefaultView;
Console.WriteLine("Test");
}
bool CanUpdateLocationView(object parameter) {
return true;
}
public ICommand UpdateLocations { get { return new RelayCommand<object>(param => this.UpdateLocationView(param), param => this.CanUpdateLocationView(param)); } }
void OpenIPAddress() {
System.Windows.Application.Current.MainWindow.Content = new AddStaticIPAddress();
}
bool CanOpenIPAddress() {
return true;
}
public ICommand OpenAddNewIPAddress { get { return new RelayCommand(OpenIPAddress,CanOpenIPAddress); } }
#endregion
}
Page AddStaticIPAddress:
<Page x:Class="StaticIPConfiger.AddStaticIPAddress"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:StaticIPConfiger"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500"
Title="AddStaticIPAddress">
<Page.DataContext>
<local:AddCustomerVModel/>
</Page.DataContext>
<Grid>
<Label x:Name="lbl_net" Content="Netz:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="lbl_subnetmask" Content="Subetnmaske:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,36,0,0"/>
<Label x:Name="label_ipaddress" Content="IP Addresse:" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="text_net" Text="" HorizontalAlignment="Left" Height="23" Margin="103,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="text_subetnmask" Text="" HorizontalAlignment="Left" Height="23" Margin="103,36,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="text_ipaddress" Text="" HorizontalAlignment="Left" Height="23" Margin="103,62,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label x:Name="lbl_description" Content="Bezeichnung:" HorizontalAlignment="Left" Margin="10,85,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="text_description" Text="" HorizontalAlignment="Left" Height="23" Margin="103,88,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Button x:Name="btn_save_add_ip" Content="IP Addresse hinzufügen" Command ="" HorizontalAlignment="Left" Margin="10,129,0,0" VerticalAlignment="Top" Width="133"/>
<Button x:Name="btn_abort_add_ip" Content="Verwerfen" HorizontalAlignment="Left" Margin="148,129,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
AddIPAddressVModel:
namespace StaticIPConfiger.Modelle {
class AddIPAddressVModel : INotifyPropertyChanged {
IPAddress _ipaddress;
public AddIPAddressVModel() {
_ipaddress = new IPAddress { Net = "", Subetnmask = "", IpAddress = ""};
}
public IPAddress IPAddress {
get { return _ipaddress; }
set { _ipaddress = value; }
}
#region"Get/Set"
public int Id {
get { return IPAddress.Id; }
set {
IPAddress.Id = value;
RaisePropertyChanged("IPAddress");
}
}
public String Net {
get { return IPAddress.Net; }
set {
IPAddress.Net= value;
RaisePropertyChanged("Net");
}
}
public string Subnetmask {
get { return IPAddress.Subetnmask; }
set {
IPAddress.Subetnmask = value;
RaisePropertyChanged("Subetnmask");
}
}
public string IpAddress {
get { return IPAddress.IpAddress; }
set {
IPAddress.IpAddress = value;
RaisePropertyChanged("IPAddress");
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Methods
private void RaisePropertyChanged(string propertyName) {
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
I hope you understand my Problem ;-)
If you have any questions, let me know.
Thanks!
EDIT:
How I open AddIPAddress
I did bind a Command to the DataGrid, where I have a Command OpenAddNewIPAddress:
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding OpenAddNewIPAddress}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=SelectedItem}"/>
</DataGrid.InputBindings>
The command looks like this:
void OpenIPAddress() {
System.Windows.Application.Current.MainWindow.Content = new AddStaticIPAddress();
}
bool CanOpenIPAddress() {
return true;
}
public ICommand OpenAddNewIPAddress { get { return new RelayCommand(OpenIPAddress,CanOpenIPAddress); } }
That another question I had. When I set the content of my MainWindow (System.Windows.Application.Current.MainWindow.Content = new AddStaticIPAddress();) with a new Page, the menubar gets lost... Is there a better way to do this?
You could inject the AddIPAddressVModel with the command parameter. Something like this:
<DataGrid IsReadOnly="True" Name="dg2Customers" ItemsSource="{Binding AlleStandorte}" AutoGenerateColumns="False" Margin="0,168,0,10" >
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding OpenAddNewIPAddress}"CommandParameter="{Binding SelectedItem}"/>
</DataGrid.InputBindings>
<DataGrid.Columns>
<DataGridTextColumn Header="StandortId" Binding="{Binding Path=l_id}" Width="*"></DataGridTextColumn>
<DataGridTextColumn Header="Standort" Binding="{Binding Path=l_name}" Width="*"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
void OpenIPAddress(object parameter)
{
System.Windows.Application.Current.MainWindow.DataContext = new AddIPAddressVModel(parameter as DataRowView)
System.Windows.Application.Current.MainWindow.Content = new AddStaticIPAddress();
}
bool CanOpenIPAddress(object parameter)
{
return true;
}
public ICommand OpenAddNewIPAddress { get { return new RelayCommand<object>(OpenIPAddress, CanOpenIPAddress); } }
public AddIPAddressVModel(DataRowView drv) {
_ipaddress = new IPAddress { Net = "", Subetnmask = "", IpAddress = ""};
_drv = drv; //keep a reference to the clicked DataRowView
}
Remove this from the AddStaticIPAddress view:
<Page.DataContext>
<local:AddCustomerVModel/>
</Page.DataContext>
The Page should inherit its DataContext. Setting the DataContext in the XAML markup is only useful in very simple scenarios when the view model has no dependencies.
Try to put a field in AddStaticIPAddress:
public DataGridRow Row { get; set; }
So you can access it from VModel.
E.g. you can initialize AddIPAddressVModelon DataGridSelectionChanged action:
private void dgCustomers_OnSelectionChanged(...)
{
AddIPAddressVModel addip = new AddIPAddressVModel();
addip.Row = (DataGridRow) dgCustomers.SelectedItem;
addip.Show();
}
this is xaml part
<ItemsControl x:Name="EventsTop">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,1,0,0">
<Button Content="{Binding Name}" Template="{DynamicResource ButtonFirst}" Height="50" Margin="15,0,0,0" Padding="10,0,15,0" FontSize="19" FontFamily="/Resources/Fonts/Font Awesome/#FontAwesome" BorderThickness="5,0,0,0" BorderBrush="#8CC152" Background="#2980B9" HorizontalContentAlignment="Left" Foreground="Black" Click="TabOpen" Tag="{Binding Id}"></Button>
<StackPanel Background="#2980B9" Margin="15,0,0,5" Visibility="Collapsed" AllowDrop="True" Tag="{Binding Id}" Drop="RowDrop">
<Border BorderThickness="5,0,0,0" BorderBrush="#8CC152">
<StackPanel>
<DockPanel LastChildFill="False">
<Label DockPanel.Dock="Left" Width="140" Content="Date" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label DockPanel.Dock="Left" Width="190" Content="Event" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label DockPanel.Dock="Left" Width="100" Content="Select" FontSize="19" BorderThickness="0,0,0,1" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
</DockPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="150">
<ItemsControl ItemsSource="{Binding Details}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="False">
<Label Content="{Binding Date}" DockPanel.Dock="Left" Width="140" FontSize="19" BorderThickness="0" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Label Content="{Binding EventName}" DockPanel.Dock="Left" Width="165" FontSize="19" BorderThickness="0" FontFamily="/Resources/Fonts/Open Sans/#Open Sans" BorderBrush="Black" HorizontalContentAlignment="Center"></Label>
<Border Width="97">
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding Checked}"></CheckBox>
</Border>
<Button Width="25" DockPanel.Dock="Left" Content="" BorderThickness="0" Background="Transparent" FontFamily="/Resources/Fonts/Font Awesome/#FontAwesome"></Button>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
this is xaml.cs
private void WindowLoaded(object sender, RoutedEventArgs e)
{
EventHelper eventHelper = new EventHelper();
TopEvents = eventHelper.GetSports(EventHelper.EventGroup.Top);
foreach (Sport item in TopEvents)
{
item.Name = "\uf196 " + item.Name;
}
EventsTop.ItemsSource = TopEvents;
AllEvents = eventHelper.GetSports(EventHelper.EventGroup.All);
foreach (Sport item in AllEvents)
{
item.Name = "\uf196 " + item.Name;
}
EventsAll.ItemsSource = AllEvents;
Sport.ItemsSource = eventHelper.GetSports(EventHelper.EventGroup.All);
}
private void RowMouseDown(object sender, MouseButtonEventArgs e)
{
DockPanel currentRow = (DockPanel) sender;
int rowOffset = Convert.ToInt32(currentRow.Tag);
DragDrop.DoDragDrop(currentRow,rowOffset,DragDropEffects.Copy);
}
private void RowDrop(object sender, DragEventArgs e)
{
int rowOffset = (int) e.Data.GetData(typeof (int));
AllEvents[0].Name = "1";
}
Also my model in collection
class Sport : INotifyPropertyChanged
{
private int _id;
private string _name = string.Empty;
private ObservableCollection<Details> _details = new ObservableCollection<Details>();
public int Id
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Content");
}
}
public ObservableCollection<Details> Details
{
get { return _details; }
set { _details = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
MessageBox.Show(info);
}
}
}
So when I am changing property its throwing MessageBox but not updating GUI.
I xaml.cs I am calling methods GetEvents thats
return ObservableCollection
I want to change Name in Sport which is in ObservableCollaction<Sport> AllEvents
You can see it in RowDrop method in xaml.cs
In debugging I notice that AllEvents[0].Name was changed but view was not updating
UPDATE
Part of ObservabelCollection declaration
public MainPage()
{
InitializeComponent();
AllEvents = new ObservableCollection<Sport>();
TopEvents = new ObservableCollection<Sport>();
EventsTop.ItemsSource = TopEvents;
EventsAll.ItemsSource = AllEvents;
}
private ObservableCollection<Sport> AllEvents;
private ObservableCollection<Sport> TopEvents;
UPDATE SECOND
I caught that when I am using window activated event it is working
I found solution.
So, ObservableCollection is working very well but,It needs to be refreshed
for appearing in view and for it we need to use
CollectionViewSource.GetDefaultView(ObservableCollection).Refresh()
method for it
I think it will help someone
The problem with property named passed to NotifyPropertyChanged method. The name of parameter should be property name. Please change the Name property as
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
Use CallerMemberNameAttribute to avoid having to get the name correct and allowing refactoring:
private void NotifyPropertyChanged([CallerMemberName] string info = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
MessageBox.Show(info);
}
}
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged(); //now no need to specify
}
}
Every property setter should notify property change, so:
public IEnumerable<Details> Details //note IEnumerable, no calling code needs to know its concrete type
{
get { return _details; }
set
{
_details = value;
NotifyPropertyChanged();
}
}
And with an observable range collection you could do this:
private readonly ObservableRangeCollection<Details> _details = new ObservableRangeCollection<Details>();
public IEnumerable<Details> Details
{
get { return _details; }
set { _details.Replace(value); }
}
From MSDN.
Occurs when an item is added, removed, changed, moved, or the entire
list is refreshed.
The changed does not mean when child properties are changed, but when you change the item at any index.
So when you modify a collection item you will need to notify the binding that the property was changed. From your window's viewmodel after you have modified the item in the collection, you would notify that the collection was changed.
NotifyPropertyChanged("AllEvents");
I am new in MVVM, in this small app i have a listbox ,three textboxes and two buttons one is Update and another is Add. In the XAML i have done binding of all listbox columns with textboxes, according to command my update button functions properly when i change values in either of textboxes but i am not aware how to take values from textboxes and add values in collection by using command .
Here is the Xaml code.
<Grid Height="314">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView Name="ListViewEmployeeDetails" Grid.Row="1" Margin="4,109,12,23" ItemsSource="{Binding Products}" >
<ListView.View>
<GridView x:Name="grdTest">
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" Width="100"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100" />
<GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" Width="100" />
</GridView>
</ListView.View>
</ListView>
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.ID}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.Name}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.Price}" />
<Label Content="ID" Grid.Row="1" HorizontalAlignment="Left" Margin="12,12,0,274" Name="label1" />
<Label Content="Price" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,59,0,0" Name="label2" VerticalAlignment="Top" />
<Label Content="Name" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,35,0,0" Name="label3" VerticalAlignment="Top" />
<Button Content="Update" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,59,0,0" Name="btnUpdate"
VerticalAlignment="Top" Width="141"
Command="{Binding Path=UpdateCommad}"
/>
<Button Content="Add" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,17,0,0" Name="btnAdd"
VerticalAlignment="Top" Width="141"
Command="{Binding UpdateCommad}"
/>
</Grid>
And here is the Product class
public class Product : INotifyPropertyChanged
{
private int m_ID;
private string m_Name;
private double m_Price;
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public int ID
{
get
{
return m_ID;
}
set
{
m_ID = value;
OnPropertyChanged("ID");
}
}
public string Name
{
get
{
return m_Name;
}
set
{
m_Name = value;
OnPropertyChanged("Name");
}
}
public double Price
{
get
{
return m_Price;
}
set
{
m_Price = value;
OnPropertyChanged("Price");
}
}
}
here is the ViewModel Class, now i am statically adding product into m_Products.
class ProductViewModel
{
private ObservableCollection<Product> m_Products;
public ProductViewModel()
{
m_Products = new ObservableCollection<Product>
{
new Product {ID=1, Name ="Pro1", Price=10},
new Product{ID=2, Name="BAse2", Price=12}
};
}
public ObservableCollection<Product> Products
{
get
{
return m_Products;
}
set
{
m_Products = value;
}
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater();
return mUpdater;
}
set
{
mUpdater = value;
}
}
private ICommand addUpdater;
public ICommand AddCommand
{
get
{
if (addUpdater == null)
addUpdater = new Updater();
return addUpdater;
}
set
{
addUpdater = value;
}
}
private class Updater : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
}
#endregion
}
}
Now i dont know how to add values(Product) into collection by using command on Add button click.
You can use the relay command. It allows you to inject the command's logic via delegates passed into its constructor:
/// <summary>
/// Class representing a command sent by a button in the UI, defines what to launch when the command is called
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
//[DebuggerStepThrough]
/// <summary>
/// Defines if the current command can be executed or not
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
With this type of command its very easy do what you want, for example in your viewmodel you can do this now:
public class ProductViewModel : INotifyPropertyChanged
{
private ObservableCollection<Product> m_Products;
public ProductViewModel()
{
m_Products = new ObservableCollection<Product>
{
new Product {ID = 1, Name = "Pro1", Price = 10},
new Product {ID = 2, Name = "BAse2", Price = 12}
};
}
private Product _selectedProduct;
public Product SelectedProduct
{
get
{
return _selectedProduct;
}
set
{
_selectedProduct = value;
OnPropertyChanged("SelectedProduct");
}
}
public ObservableCollection<Product> Products
{
get
{
return m_Products;
}
set
{
m_Products = value;
}
}
ICommand _addCommand;
public ICommand AddCommand
{
get
{
if (_addCommand == null)
{
_addCommand = new RelayCommand(param => AddItem());
}
return _addCommand;
}
}
ICommand _deleteCommand;
public ICommand DeleteCommand
{
get
{
if (_deleteCommand == null)
{
_deleteCommand = new RelayCommand(param => DeleteItem((Product)param));
}
return _deleteCommand;
}
}
private void DeleteItem(Product product)
{
if (m_Products.Contains(product))
{
m_Products.Remove(product);
}
}
private void AddItem()
{
m_Products.Add(new Product());
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
As you can see, there are two commands, one to add a Product and other to delete the selected product.You don't have to worry about the update, you are using an ObservableCollection<>.Also, I add the property selectedProduct to your ViewModel to know what element was selected in your view:
<Grid Height="314">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView Name="ListViewEmployeeDetails" Grid.Row="1" Margin="4,109,12,23" ItemsSource="{Binding Products}" SelectedValue="{Binding SelectedProduct}" >
<ListView.View>
<GridView x:Name="grdTest">
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" Width="100" />
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100" />
<GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" Width="100" />
</GridView>
</ListView.View>
</ListView>
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding SelectedProduct.ID}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding SelectedProduct.Name}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="{Binding SelectedProduct.Price}" />
<Label Content="ID" Grid.Row="1" HorizontalAlignment="Left" Margin="12,12,0,274" Name="label1" />
<Label Content="Price" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,59,0,0" Name="label2" VerticalAlignment="Top" />
<Label Content="Name" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,35,0,0" Name="label3" VerticalAlignment="Top" />
<Button Content="Remove" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,59,0,0" Name="btnUpdate"
VerticalAlignment="Top" Width="141"
Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedProduct}"
/>
<Button Content="Add" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,17,0,0" Name="btnAdd"
VerticalAlignment="Top" Width="141"
Command="{Binding AddCommand}"
/>
</Grid>
In the delete button I declare the CommandParamameter and I bound it to the SelectedProduct property. This is the param that receive the RelayCommand to delete a product. It is no necessary, you have already in the viewmodel the SelectedProduct, but I did it anyway to show how you can pass a parameter to a command.
[EDIT 1]
To achieve the behavior that you want you need to add three new properties in your viewModel (Id, Name, and Price).Now those properties should be bounded with TextBoxes. To edit a selected product in your ListView, in the set of the SelectedProduct property you need to set too the values of the ID, Name and Prices properties. You have to set the properties of the selected product when a textbox change its value too.
Changes in the ViewModel:
private int _id=1;
public int Id
{
get
{
return _id;
}
set
{
_id = value;
if (SelectedProduct!=null)
{
SelectedProduct.ID = _id;
}
OnPropertyChanged("Id");
}
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
if (SelectedProduct != null)
{
SelectedProduct.Name = _name;
}
OnPropertyChanged("Name");
}
}
private double _price = 0;
public double Price
{
get
{
return _price;
}
set
{
_price = value;
if (SelectedProduct != null)
{
SelectedProduct.Price = _price;
}
OnPropertyChanged("Price");
}
}
private Product _selectedProduct;
public Product SelectedProduct
{
get
{
return _selectedProduct;
}
set
{
_selectedProduct = value;
Id = _selectedProduct != null ? _selectedProduct.ID : 0;
Name = _selectedProduct != null ? _selectedProduct.Name : "";
Price = _selectedProduct != null ? _selectedProduct.Price : 0;
OnPropertyChanged("SelectedProduct");
}
}
Changes in your View:
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding Id}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding Name}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="{Binding Price}" />