ListBox SelectedItem Binding Broken - wpf

I am using WPF having a strange issue with RadListBox SelectedItem databinding, trying to figure out but no luck. Following is my scenario
I am using Telerik Controls (RadListBox, and RadButton)
RadButton is placed inside a ItemsControl, RadListBox and ItemsControl are bind to same ItemsSource.
I am using PRISM and MVVM.
What I want is when I click on button, the same item is selected from RadListBox automatically, (This part working fine).
Problem: As soon as I click on any item of RadListBox and then click back on any button the item selection stops working.
Edit: I tried the same thing with standard WPF ListBox by adding attached behavior for selection changed event and attached property of Command and CommandParameter, it works fine, so it looks like an issue with Telerik RadListBox ?
Now let me come to code.
ViewModel Class
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public DelegateCommand<object> StudentSelected { get; set; }
public DelegateCommand<object> ButtonPressed { get; set; }
private void OnStudentSelected(object par)
{
//Debugger.Break();
if (handled == false)
{
Student std = par as Student;
if (std != null)
{
SelectedStudent = std;
}
}
handled = false;
}
private void OnButtonPressed(object par)
{
//Debugger.Break();
handled = true;
String std = par as String;
if (std != null)
{
foreach (Student st in _students)
{
if (st.Name.Equals(std))
{
SelectedStudent = st;
break;
}
}
}
}
private Student _selectedstudent;
private bool handled = false;
public MainViewModel()
{
StudentSelected = new DelegateCommand<object>(OnStudentSelected);
ButtonPressed = new DelegateCommand<object>(OnButtonPressed);
}
public Student SelectedStudent
{
get
{
return _selectedstudent;
}
set
{
_selectedstudent = value;
OnPropertyChanged("SelectedStudent");
}
}
private ObservableCollection<Student> _students;
public ObservableCollection<Student> Students
{
get
{
return _students;
}
set
{
_students = value;
OnPropertyChanged("Students");
}
}
}
public class Student
{
public String Name { get; set; }
public String School { get; set; }
}
MainView XAML
<telerik:RadListBox Grid.Column="0" Grid.Row="0" ItemsSource="{Binding Students}" Command="{Binding StudentSelected}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=SelectedItem}" SelectedItem="{Binding SelectedStudent, Converter={StaticResource DebugConverter}}">
<!-- The above debug converter is just for testing binding, as long as I keep on clicking button the Converter is being called, but the moment I click on RadListBoxItem the Converter is not called anymore, even when I click back on buttons -->
<telerik:RadListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</telerik:RadListBox.ItemTemplate>
</telerik:RadListBox>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding SelectedStudent.Name}"></Label>
<StackPanel Grid.Column="1" Grid.Row="1" Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Students}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<telerik:RadButton Width="100" Height="70" Content="{Binding Name}" Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=DataContext.ButtonPressed}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Content}">
</telerik:RadButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
and finally populating the ViewModel and setting the Datacontext
MainViewModel mvm = new MainViewModel();
ObservableCollection<Student> students = new ObservableCollection<Student>();
students.Add(new Student { Name = "Student 1", School = "Student 1 School" });
students.Add(new Student { Name = "Student 2", School = "Student 2 School" });
students.Add(new Student { Name = "Student 3", School = "Student 3 School" });
mvm.Students = students;
//Bind datacontext
this.DataContext = mvm;
Please give your suggestions and share you expertise from WPF Jargon?

Finally I figured out the issue, I just need to replace the RadListBox SelectedItem binding to TwoWay
<telerik:RadListBox Grid.Column="0" Grid.Row="0" ItemsSource="{Binding Students}" Command="{Binding StudentSelected}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=SelectedItem}" SelectedItem="{Binding SelectedStudent, Mode,TwoWay, Converter={StaticResource DebugConverter}}">

Related

Wpf set edit mode for all cells in datagridrow MVVM

Using the MVVM pattern in a WPF application, I want to set on the 'Editing State' of a records in row.
Every time the user starts editing a record by clicking on Edit button, that row should switch to the 'editing' mode.
Finished, he can save all changes in the row by clicking the same or another button
How can I set edit mode (IsReadOnly=true/false) for All cells in selected Row on click "Edit" button?
Any help is appreciated!
This is my current code:
XAML
<Window x:Class="TotalRows.TotalRowsWindow"
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:TotalRows"
mc:Ignorable="d"
x:Name="xMainWindow"
Title="RowsTotalWindow" Height="450" Width="800">
<Window.DataContext>
<local:ExampleData/>
</Window.DataContext>
<Grid>
<StackPanel >
<DataGrid x:Name="myGrid" IsReadOnly="True" CanUserAddRows="False" SelectionMode="Single" CanUserDeleteRows="False"
ItemsSource="{Binding ItemsViewCollection}" RowDetailsVisibilityMode="Collapsed"
SelectedItem="{Binding SelectedItemRow, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel HorizontalAlignment="Stretch">
<ToggleButton x:Name="btnEditItem" Content="Edit" Width="50" Height="20" Margin="0 0 3 0"
Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.UpdateItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=DataContext}"/>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn x:Name="gr" Binding="{Binding Group}" Header="Gr" Width="30" />
<DataGridTextColumn x:Name="one" Binding="{Binding Col_1}" Header="h1" Width="30" />
<DataGridTextColumn x:Name="two" Binding="{Binding Col_2}" Header="h2" Width="30" />
<DataGridTextColumn x:Name="tree" Binding="{Binding Col_3}" Header="h3" Width="30" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
</Window>
Code Behind
namespace TotalRows
{
public class ItemClass
{
public int Group { get; set; }
public string Title { get; set; }
public int Col_1 { get; set; }
public int Col_2 { get; set; }
public int Col_3 { get; set; }
}
public class ExampleData
{
private bool _IsReadMode;
public bool IsReadMode
{
get { return _IsReadMode; }
set
{
_IsReadMode = value;
OnPropertyChanged(nameof(IsReadMode));
}
}
private ItemClass _selectedItem = null;
public ItemClass SelectedItemRow
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged(nameof(SelectedItemRow));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<ItemClass> _items;
public ObservableCollection<ItemClass> Items
{
get
{
return _items;
}
set
{
if (_items != value)
{
_items = value;
OnPropertyChanged(nameof(Items));
}
}
}
private ICollectionView _itemsViewCollection;
public ICollectionView ItemsViewCollection
{
get
{
return _itemsViewCollection;
}
set
{
if (_itemsViewCollection != value)
{
_itemsViewCollection = value;
OnPropertyChanged(nameof(ItemsViewCollection));
}
}
}
public ICommand UpdateItemCommand { get; private set; }
public ExampleData()
{
IsReadMode = true;
UpdateItemCommand = new ViewModelCommand(param => updateItemCommand(param));
Items = new ObservableCollection<ItemClass>()
{
new ItemClass() {Group=1, Title="Item1", Col_1=100, Col_2=150, Col_3=250},
new ItemClass() {Group=2, Title="Item1", Col_1=50, Col_2=2, Col_3=200},
new ItemClass() {Group=2, Title="Item2", Col_1=50, Col_2=100, Col_3=40},
new ItemClass() {Group=3, Title="Item1", Col_1=60, Col_2=25, Col_3=230},
new ItemClass() {Group=3, Title="Item2", Col_1=30, Col_2=25, Col_3=0},
new ItemClass() {Group=3, Title="Item3", Col_1=9, Col_2=100, Col_3=20},
new ItemClass() {Group=4, Title="Item1", Col_1=46, Col_2=32, Col_3=30},
};
ItemsViewCollection = CollectionViewSource.GetDefaultView(Items);
ItemsViewCollection.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
}
private void updateItemCommand(object param)
{
IsReadMode = !IsReadMode;
}
}
}
Do you realise f2 switches the current row into edit mode?
CommandManager.RegisterClassInputBinding(ownerType, new InputBinding(BeginEditCommand, new KeyGesture(Key.F2)));
CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(BeginEditCommand, new ExecutedRoutedEventHandler(OnExecutedBeginEdit), new CanExecuteRoutedEventHandler(OnCanExecuteBeginEdit)));
You could bind your edit button to a command in the ExampleData viewmodel and pass a reference to the specific ItemClass as a command parameter.
Use relativesource binding to get to that command.
ExampleData owns that collection so you can set properties on that instance and stash a reference or index to the last one they edited set the flag back. Or iterate through the whole collection.
Seems you know how to write a command but I recommend the community mvvm toolkit and relaycommand.
Your binding would be something like
<Button Command="{Binding DataContext.EditThisOneCommand
, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}">
The command parameter passes the row to an Icommand so will be a parameter passed to your command.
A similar command I happen to have.
private RelayCommand<Thing> _colourCommand;
public RelayCommand<Thing> ColourCommand
{
get
{
return _colourCommand
?? (_colourCommand = new RelayCommand<Thing>(
_thing =>
{
_thing.Row = Items.IndexOf(_thing);
},
_thing => CanUserClick));
}
}
You would of course have EditThisOneCommand
You then have to tell the UI to issue a BeginEditCommand.
And then you need to tell the UI to issue a CommitEditCommand when you finish.
CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(CommitEditCommand, new ExecutedRoutedEventHandler(OnExecutedCommitEdit), new CanExecuteRoutedEventHandler(OnCanExecuteCommitEdit)));
These commands are source from the datagrid.
You could instead just bind those datagrid commands to buttons and not have this flag.
A datagridrow has a property IsEditing. You might be able to bind that onewaytosource to your flag. You'd set that binding via a style.
Not sure why you'd want to, but you could take a look at that.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.datagridrow.isediting?view=windowsdesktop-7.0

How to prevent user from selecting the same value from more than one combobox in wpf

I have a simple class as such:
public class Item
{
public int ID{get;set;}
public string Name{get;set;}
}
I have a List of this class in my Mainwindow.xaml.cs as such:
public List<Item> AllItems=GetAllItems();
I have four properties of Item class in my Mainwindow.xaml.cs as such:
public Item Item1{get;set;}
public Item Item2{get;set;}
public Item Item3{get;set;}
public Item Item4{get;set;}
This List:AllItems is databinded to four Comboboxes as under:
<ComboBox x:Name="cmbCode1" ItemsSource="{Binding AllItems}" DisplayMemberPath="ID" SelectedValuePath="ID" SelectedValue="{Binding Item1.ID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Item1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox x:Name="cmbCode2" ItemsSource="{Binding AllItems}" DisplayMemberPath="ID" SelectedValuePath="ID" SelectedValue="{Binding Item1.ID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Item1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox x:Name="cmbCode3" ItemsSource="{Binding AllItems}" DisplayMemberPath="ID" SelectedValuePath="ID" SelectedValue="{Binding Item1.ID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Item1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox x:Name="cmbCode4" ItemsSource="{Binding AllItems}" DisplayMemberPath="ID" SelectedValuePath="ID" SelectedValue="{Binding Item1.ID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Item1,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
I have four TextBoxes corresponding to these four Comboboxes as such:
<TextBlock x:Name="txtName1" Text="{Binding Item1.Name,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock x:Name="txtName2" Text="{Binding Item2.Name,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock x:Name="txtName3" Text="{Binding Item3.Name,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock x:Name="txtName4" Text="{Binding Item4.Name,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
What i want is that the user should never be able to select the same ID from more than one combobox.
Is there some simple way that could be done,especially using xaml only?How can i hide or show the items selected/unselected from other comboboxes so that the user can't select the same ID from more than one combobox ever?
So far i have tried to send the selected Item and the entire List to a MultivalueConverter and eliminating/adding items to the Lists there itself,but this seems too meesy.Any other better idea would be appreciated.
You could have a separate list for each of your ComboBoxes. You could then add a LostFocus event handler to each of them. You could use this to repopulate the lists for the other ComboBoxes to exclude the selection.
For example, if I've got 5 items in my list; initially I'll be able to select all 5 in any of my ComboBoxes. When I select Item1 in ComboBox1 the LostFocus event handler will update the lists behind ComboBoxes 2-4 to remove Item1. When I then select Item2 in ComboBox2 the LostFocus event handler will then update the lists behind ComboBoxes 3 and 4 to remove Item2. And so on...
An alternative approach might be to let the user select whatever they like and then run some kind of validation on the selected values to make sure that they're unique. This article goes through some of your options.
Personally, I'd go with the second approach; perhaps with a message above the textboxes indicating that the selection must be unique. You could indicate any errors and block any actions that rely on the selection while it's invalid but you're not having to update your data constantly which will probably lead to a smoother UI.
You can use the code to hide the selected item in different combobox
for (int count = 0; count <= cmb1.Items.Count -1; count++)
{
if((ComboBoxItem)(cmb1.Items[count])).SelectedValue==TextBox1.Text)
((ComboBoxItem)(cmb1.Items[count])).Visibility = System.Windows.Visibility.Collapsed;
}
This code you can write in selected event of the comboxes.
I guess you can write the same logic using triggers in XAML
You should handle this kind of logic in your view models. Here is an example for you that should give you the idea:
View Model:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
AllItems = new List<Item>() { new Item { Name = "1" }, new Item { Name = "2" }, new Item { Name = "3" }, new Item { Name = "4" } };
}
public List<Item> AllItems { get; set; }
private Item _item1;
public Item Item1
{
get { return _item1; }
set
{
_item1 = value;
NotifyPropertyChanged();
if (value != null)
value.CanSelect = false;
}
}
private Item _item2;
public Item Item2
{
get { return _item2; }
set
{
_item2 = value;
NotifyPropertyChanged();
if (value != null)
value.CanSelect = false;
}
}
private Item _item3;
public Item Item3
{
get { return _item3; }
set
{
_item3 = value; NotifyPropertyChanged();
if (value != null)
value.CanSelect = false;
}
}
private Item _item4;
public Item Item4
{
get { return _item4; }
set
{
_item4 = value;
NotifyPropertyChanged();
if (value != null)
value.CanSelect = false;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Item : INotifyPropertyChanged
{
public int ID { get; set; }
public string Name { get; set; }
private bool _canSelect = true;
public bool CanSelect
{
get { return _canSelect; }
set { _canSelect = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Style x:Key="icstyle" TargetType="ComboBoxItem">
<Setter Property="IsEnabled" Value="{Binding CanSelect}" />
</Style>
...
<ComboBox x:Name="cmbCode1" ItemsSource="{Binding AllItems}" SelectedItem="{Binding Item1}" DisplayMemberPath="Name" ItemContainerStyle="{StaticResource icstyle}"/>
<ComboBox x:Name="cmbCode2" ItemsSource="{Binding AllItems}" SelectedItem="{Binding Item2}" DisplayMemberPath="Name" ItemContainerStyle="{StaticResource icstyle}"/>
<ComboBox x:Name="cmbCode3" ItemsSource="{Binding AllItems}" SelectedItem="{Binding Item3}" DisplayMemberPath="Name" ItemContainerStyle="{StaticResource icstyle}"/>
<ComboBox x:Name="cmbCode4" ItemsSource="{Binding AllItems}" SelectedItem="{Binding Item4}" DisplayMemberPath="Name" ItemContainerStyle="{StaticResource icstyle}"/>

Unselect WPF DataGrid when ItemsSource changed

I have the following DataGrid in WPF :
<DataGrid x:Name="dgPatientMedicationOrderList" Width="Auto" HorizontalAlignment="Stretch" RowHeight="40" Background="Transparent" HorizontalContentAlignment="Left"
GridLinesVisibility="None" RowHeaderWidth="0" VirtualizingStackPanel.VirtualizationMode="Standard" SelectedIndex="-1"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" AutoGenerateColumns="False" SelectionMode="Single"
IsSynchronizedWithCurrentItem="True" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Stretch" IsReadOnly="True"
ItemsSource="{Binding PatientOrdersCollectionView}">
When a user clicks on a row in the DataGrid, it's SelectionChanged event is fired and the bound ViewModel command drives the View to load another user control corresponding to the DG row. In my view I am changing my datagrid's source binding from my ViewModel. Now the problem is that every time the ItemsSource is changed the SelectionChanged event is fired selecting the first item in the DataGrid; this is followed by the view loading the user control without the user explicitly selecting the DataGrid row. How can I prevent the DataGrid from selecting any Row when it's ItemsSource is changed ?
Simplified Demo Code:
XAML:
<Button Content="Change Source" Command="{Binding ChangeItemsSourceCmd}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,20" />
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Orientation="Vertical">
<TextBlock Text="{Binding SelectedPerson.Id, StringFormat=ID: {0}}" />
<TextBlock Text="{Binding SelectedPerson.Name, StringFormat=Name: {0}}" />
<TextBlock Text="{Binding SelectedPerson.Gender, StringFormat=Gender: {0}}" />
<TextBlock Text="{Binding SelectedPerson.Country, StringFormat=Country: {0}}" />
</StackPanel>
</Grid>
ViewModel:
public class WindowViewModel : ViewModelBase
{
private Person _selectedPerson;
private ObservableCollection<Person> _personList;
public Person SelectedPerson
{
get { return _selectedPerson; }
set { RaisePropertyChange<Person>(() => SelectedPerson, ref _selectedPerson, ref value); }
}
public ObservableCollection<Person> PersonList
{
get { return _personList; }
set
{
SelectedPerson = null;
RaisePropertyChange<ObservableCollection<Person>>(() => PersonList, ref _personList, ref value);
}
}
public WindowViewModel()
{
PersonList = new ObservableCollection<Person>()
{
new Person() { Id=101, Name="Mahesh", Gender="Male", Country="India"},
new Person() { Id=102, Name="Srinivas", Gender="Male", Country="Sri Lanka"},
new Person() { Id=103, Name="Isha", Gender="Female", Country="United States"},
new Person() { Id=104, Name="Salim", Gender="Male", Country="Pakistan"}
};
}
public ICommand ChangeItemsSourceCmd
{
get
{
return new RelayCommand(ChangeItemsSourceCmdHandler);
}
}
private void ChangeItemsSourceCmdHandler()
{
PersonList = new ObservableCollection<Person>()
{
new Person() { Id=105, Name="Raman", Gender="Male", Country="Uganda"},
new Person() { Id=106, Name="Anurag", Gender="Male", Country="England"},
new Person() { Id=107, Name="Komal", Gender="Female", Country="Thailand"},
new Person() { Id=108, Name="Nitin", Gender="Male", Country="Africa"}
};
}
}
You should:
1.Add a SelectedItem Binding in your DataGrid:
SelectedItem="{Binding Selected, Mode=TwoWay}"
2.Have the related property (firing PropertyChanged of course)
public object Selected
{
get { return selected; }
set
{
selected = value;
OnPropertyChanged("Selected");
}
}
3.Set it to null in your Itemssource setter (or before you change it)
public IEnumerable PatientOrdersCollectionView
{
get { return patientOrdersCollectionView; }
set
{
Selected = null; // Put it to null here to unselect it from grid
patientOrdersCollectionView = value;
OnPropertyChanged("PatientOrdersCollectionView");
}
}
Should do the trick.

execute a function in a viewmodel and real time refresh of the view

I have a small problem with my MVVM application.
I have a function in a viewmodel which modify a collection. This collection is bind to the view to show a datagrid. When an user click on a button, the function modify the collection but it can take few minutes and the view is not refresh.
My question is how can I execute this function to have the view refreshed in real time ?
In another program I have used the dispatcher but it was in the code behind of the view without binding.
Thanks
Edit :
Model :
public class Composants : INotifyPropertyChanged
{
private string _nom;
public string Nom
{
get { return _nom; }
set { _nom = value; OnPropertyChanged("Nom"); }
}
}
ViewModel :
public class PageSynchroViewModel : INotifyPropertyChanged
{
public void SynchroniserComposants()
{
foreach (var comp in _selectedVersion.ListeComposants)
{
comp.Nom = "";
}
}
View (I don't put all the code):
<Page x:Class="Centre_de_synchronisation.Vues.PageSynchro"
[...]
xmlns:app="clr-namespace:Centre_de_synchronisation.Classes" mc:Ignorable="d"
d:DesignHeight="531" d:DesignWidth="778"
Title="PageSynchro" Background="{x:Null}">
<Canvas>
[...]
<DataGrid Name="GridComposants" Style="{StaticResource DatagridStyle}" ItemsSource="{Binding ListeComposants}" AutoGenerateColumns="False" Canvas.Left="12" Canvas.Top="201" Height="285" Width="754" >
<DataGrid.Columns>
<DataGridTextColumn
Header="Nom"
Binding="{Binding Nom}"
Width="150"
IsReadOnly="True"/>
[...]
</DataGrid>
<Button Name="BoutonSynchro" Style="{StaticResource MessageBoxButtonStyle}" Content="Synchroniser" Height="27" Width="107" Command="{Binding BoutonSynchro}" CommandParameter="GridComposants" Visibility="{Binding Etat, Converter={StaticResource VisibilityConverter}}"/>
</Canvas>
Try using an ObservableCollection<T> instead of the collection you are using now.
This should cause the View to be updated whenever an Item is added or removed from the collection.
Just remember when interacted with the ObservableCollection to Invoke the Dispatcher otherwise you will get Thread Access Exceptions
Here is the code I did to test this.
XAML
<Window.Resources>
<loc:MyViewModel x:Key="ViewModel" />
</Window.Resources>
<Canvas DataContext="{StaticResource ViewModel}">
<DataGrid ItemsSource="{Binding Collection}"
Width="150"
Height="200">
<DataGrid.Columns>
<DataGridTextColumn Header="Nom"
Binding="{Binding Nom}"
Width="150"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
<Button Command="{Binding DoStuffCommand}"
Canvas.Bottom="0"
Canvas.Right="0">Stuff</Button>
</Canvas>
ViewModel
public class MyViewModel
{
public ObservableCollection<MyModel> Collection { get; set; }
public ICommand DoStuffCommand { get; set; }
public MyViewModel()
{
this.Collection = new ObservableCollection<MyModel>();
for (int i = 0; i < 10; i++)
{
Collection.Add(new MyModel { Nom = i.ToString() });
}
this.DoStuffCommand = new RelayCommand(DoStuff);
}
private void DoStuff()
{
foreach (var item in Collection)
{
item.Nom = item.Nom + ".";
}
}
}
Model
public class MyModel : INotifyPropertyChanged
{
private string nom;
public string Nom
{
get { return nom; }
set
{
nom = value;
RaiseChanged("Nom");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
It updated the Nom in the view.

Silverlight MVVM Light Listbox item Click Event

I'm using the MVVM Light Toolkit with Silverlight.
On my UserControl I have a ListBox that displays a list of files. Each file has a delete image next to the file name. In the DataTemplate for the listbox I have an image (or can use a button) and a TextBlock.
So I want to capture using the event when the user will clicks on the image(or button with image) to remove the file from the list of files.
But I cannot seem to capture the event. Maybe this is due to having the SelectedItem Event on the listbox?
public class MainViewModel : ViewModelBase
{
#region Properties
public const string SelectedListBoxFilePropertyName = "SelectedUploadFile";
private UploadFile _selectedUploadFile = null;
public UploadFile SelectedUploadFile
{
get
{
return _selectedUploadFile;
}
set
{
if (_selectedUploadFile == value)
return;
_selectedUploadFile = value;
RaisePropertyChanged(SelectedListBoxFilePropertyName);
}
}
public const string UploadFilesPropertyName = "UploadFiles";
private ObservableCollection<UploadFile> _uploadFiles = new ObservableCollection<UploadFile>();
public ObservableCollection<UploadFile> UploadFiles
{
get
{
return _uploadFiles;
}
set
{
if (_uploadFiles == value)
return;
_uploadFiles = value;
RaisePropertyChanged(UploadFilesPropertyName);
}
}
#endregion
public static ICommand BrowseCommand { get; private set; }
public static ICommand DragDropFileCommand { get; private set; }
public static ICommand RemoveCommand { get; private set; }
#region Constructor
public MainViewModel()
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
UploadFiles = new UploadFileContainer().UploadFiles;
}
else
{
// Code runs "for real"
}
WireUpCommands();
}
#endregion
#region Event Handlers
private void OnBrowseFileCommand()
{
var dialog = new OpenFileDialog();
dialog.ShowDialog();
if (dialog.Files != null)
AddFiles(dialog.Files);
}
private void OnDropFileCommand(DragEventArgs e)
{
var files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
AddFiles(files);
}
private void OnRemoveFileCommand()
{
UploadFiles.Remove(_selectedUploadFile);
}
#endregion
#region Private Methods
private void WireUpCommands()
{
BrowseCommand = new RelayCommand(OnBrowseFileCommand);
DragDropFileCommand = new RelayCommand<DragEventArgs>(e => OnDropFileCommand(e));
RemoveCommand = new RelayCommand(OnRemoveFileCommand);
UploadCommand = new RelayCommand(OnClickUploadCommand);
}
#endregion
}
<ListBox Grid.Row="1" Height="214" HorizontalAlignment="Left" AllowDrop="True" Margin="6,26,0,0" Name="UploadFilesListBox" VerticalAlignment="Top" Width="415" ItemsSource="{Binding Path=UploadFiles}" SelectedItem="{Binding Path=SelectedListBoxFile, Mode=TwoWay}" ScrollViewer.VerticalScrollBarVisibility="Auto">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<cmd:EventToCommand Command="{Binding DragDropFileCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.Background>
<ImageBrush ImageSource="/FileUploadApplication;component/Resources/dragdrophere.png" Stretch="None" />
</ListBox.Background>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveCommand}">
<Image Source="/FileUploadApplication;component/Resources/delete.png"/>
</Button>
<Image Source="/FileUploadApplication;component/Resources/delete.png">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding RemoveCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image> <TextBlock Text=" " />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Since your ItemsSource is UploadFiles it's probably sending the event to UploadFile and not the view model the user control is bound to.
Your button is the element of the ItemTemplate. you're binding the listbox ItemsSource to the ObservableCollection. Every Itemtemplate DataContext is no MainViewModel, but UploadFile, which has no RemoveCommand.
I was solving this by adding to every item the parent object using constructor. RemoveCommand was inside the item's ViewModel and insede the remove function i was calling the parent's method to delete the item.
Not sure if that's the best solution but it worked for me.

Resources