PropetyChanged event is null on RelativeSource binding - wpf

I use on the following DataGrid:
<DataGrid SelectedValuePath="Key" DisplayMemberPath="Value" ItemsSource="{Binding MyModelGrid}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="{x:Static p:Resources.XHeader}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox IsEditable="True" SelectedItem="{Binding Value.X}" ItemsSource="{Binding DataContext.XList,RelativeSource={RelativeSource AncestorType=DataGrid,Mode=FindAncestor}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
//More details...
</DataGrid.Columns>
</DataGrid>
It's DataContext is the following Model:
public class MyModel: INotifyPropertyChanged
{
private SortedDictionary<int, GridRowObj> _myModelGrid= new SortedDictionary<int,GridRowObj>();
public SortedDictionary<int, GridRowObj> MyModelGrid
{
get
{
return _myModelGrid;
}
set
{
if (_myModelGrid!= value)
{
_myModelGrid= value;
OnPropertyChanged("MyModelGrid");
}
}
}
private ObservableCollection<string> _xList;
public ObservableCollection<string> XList
{
get
{
return _xList;
}
set
{
if (_xList!= value)
{
_xList= value;
OnPropertyChanged("XList");
}
}
}
}
The Dictonary - MyModelGrid initialized in 20 rows (they are displayed on the screen).
And when I put data to the list - XList, the PropetyChangedEvent is null, so the list is displayed empty.
I think I'm missing something in the binding. I can not understand why the event is null?
EDIT:
DataContext setting :
public MyView()
{
InitializeComponent();
MyViewModel vm = new MyViewModel();
this.DataContext = vm.Model;
}
MyViewModel.cs:
public class MyViewModel: INotifyPropertyChanged
{
private MyModel _model = new MyModel();
public MyModel Model
{
get
{
return _model;
}
set
{
_model= value;
NotifyPropertyChanged("Model");
}
}
}

Related

Caliburn micro listbox multiple selection MVVM

I have a ListBox containing Name. Now I need to select multiple items from ListBox.
ViewModel.CS
private Person selectedListOfPeople_;
public Person SelectedListOfPeople
{
get
{ return selectedListOfPeople_;}
set
{ this.SetProperty(ref selectedListOfPeople_, value, nameof(SelectedListOfPeople));}
}
private ObservableCollection<Person> listOfPeople_;
public ObservableCollection<Person> ListOfPeople
{
get { return listOfPeople_; }
set
{
this.SetProperty(ref listOfPeople_, value, nameof(ListOfPeople));
}
}
public ShellViewModel()
{
ListOfPeople = new ObservableCollection<Person>
{
new Person("ABC"),new Person("DEF"),new Person("GHI"),new Person("JKL")
};
}
public class Person : Screen
{
private string personName_;
public string PersonName
{
get { return personName_; }
set { this.SetProperty(ref personName_, value, nameof(PersonName)); }
}
public Person(string personName)
{
PersonName = personName;
}
private bool isSelected_;
public bool IsSelected
{
get { return isSelected_; }
set { this.SetProperty(ref isSelected_, value, nameof(IsSelected)); }
}
}
View.XAML
<Grid Width="500" Height="500" Background="LightBlue">
<ListBox x:Name="ListOfPeople" SelectionMode="Multiple" Height="300" Width="300" Margin="120,100,80,100">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding PersonName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
in that SelectedListOfPeople is not called when the second item is selected in ListBox set to Multiple selections. How can I make sure that this event is raised every time the user makes a selection in ListBox?
One way of doing this would be to break from the convention available in that framework and bind the property manually.
But first you would need to update the property for multiselect in the view model
private ObservableCollection<Person> selectedListOfPeople;
public ObservableCollection<Person> SelectedListOfPeople {
get { return selectedListOfPeople; }
set { this.SetProperty(ref selectedListOfPeople, value, nameof(SelectedListOfPeople)); }
}
private ObservableCollection<Person> listOfPeople;
public ObservableCollection<Person> ListOfPeople {
get { return listOfPeople; }
set { this.SetProperty(ref listOfPeople, value, nameof(ListOfPeople)); }
}
public ShellViewModel() {
ListOfPeople = new ObservableCollection<Person> {
new Person("ABC"),
new Person("DEF"),
new Person("GHI"),
new Person("JKL")
};
SelectedListOfPeople = new ObservableCollection<Person>();
}
And then bind to the desired property in the view's XAML
<ListBox x:Name="ListOfPeople" SelectionMode="Multiple"
Height="300" Width="300" Margin="120,100,80,100"
SelectedItems = "{Bining SelectedListOfPeople}"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding PersonName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
The convention will bind the items source or the ListBox and the manual binding of the SelectedItems will provided the desired behavior.

Multiple Combobox with same Itemsource but the SelectedItem should be different - WPF

Goal is to have multiple Combobox, once the item is selected in any of the Combobox, it should be removed or hided from other Comboboxes for selection.
I'm able to change the source whenever the selection got changed on any of the Comboboxes. But Problem is when I remove the selected item from the ItemsSource, item got removed from all the sources including the one on which I selected.
Here is the sample
xaml
<StackPanel>
<ComboBox Width="100" Height="50" SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding ComboboxItems}" />
<ComboBox Width="100" Height="50" SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding ComboboxItems}" />
<ComboBox Width="100" Height="50" SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding ComboboxItems}" />
<ComboBox Width="100" Height="50" SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding ComboboxItems}" />
</StackPanel>
Codebehind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<string> s = new ObservableCollection<string>();
s.Add("item1");
s.Add("item2");
s.Add("item3");
s.Add("item4");
s.Add("item5");
s.Add("item6");
DataContext = new MainViewModel() { ComboboxItems = s , SelectedItem = "item2" };
}
}
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> comboboxitems;
public ObservableCollection<string> ComboboxItems
{
get { return comboboxitems; }
set { comboboxitems = value; OnPropertyChanged("ComboboxItem"); }
}
private string _selectedItem;
public string SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
comboboxitems.Remove(value); //here removing selected item from itemssource
OnPropertyChanged("SelectedItem");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propname)
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propname));
}
}
I know I can have multiple Collection for each Combobox but that may take lot of memory if number of Combobox increases.
Hope, there should be a easy way to achieve this in WPF.
You could define different SelectedItem for each ComboBox, and then create a wrapper of the SharedItemSource for each ComboBoxto filter out the SelectedItem of other ComboBox. eg:
C# :
public IEnumerable<string> ComboboxItems1
{
get
{
return ComboboxItems.Where(x => x != SelectedItem2 && x != SelectedItem3);
}
}
public string SelectedItem1
{
get { return _selectedItem1; }
set
{
if (_selectedItem1 != value)
{
_selectedItem1 = value;
RaisePropertyChanged("SelectedItem1");
RaisePropertyChanged("ComboboxItems2"); //raise propertychanged to refresh GUI
RaisePropertyChanged("ComboboxItems3");
}
}
}
public IEnumerable<string> ComboboxItems2
{
get
{
return ComboboxItems.Where(x => x!=SelectedItem1&&x!=SelectedItem3);
}
}
public string SelectedItem2
{
get { return _selectedItem2; }
set
{
if (_selectedItem2 != value)
{
_selectedItem2 = value;
RaisePropertyChanged("SelectedItem2");
RaisePropertyChanged("ComboboxItems1"); //raise propertychanged to refresh GUI
RaisePropertyChanged("ComboboxItems3");
}
}
}
public IEnumerable<string> ComboboxItems3
{
get
{
return ComboboxItems.Where(x => x != SelectedItem1 && x != SelectedItem2);
}
}
public string SelectedItem3
{
get { return _selectedItem3; }
set
{
if (_selectedItem3 != value)
{
_selectedItem3 = value;
RaisePropertyChanged("SelectedItem3");
RaisePropertyChanged("ComboboxItems1"); //raise propertychanged to refresh GUI
RaisePropertyChanged("ComboboxItems2");
}
}
}
XAML:
<ComboBox SelectedItem="{Binding SelectedItem1}" ItemsSource="{Binding ComboboxItems1}" />
<ComboBox SelectedItem="{Binding SelectedItem2}" ItemsSource="{Binding ComboboxItems2}" />
<ComboBox SelectedItem="{Binding SelectedItem3}" ItemsSource="{Binding ComboboxItems3}" />

How remove list to listbox with Button using MVVM silverlight

My Problem, I can't remove list in my listbox from browser, but when i debug in my code data list has removed.
View Model
private List<Customer> _listCustomer = new List<Customer>();
public List<Customers> ListCustomers
{
get { return _listCustomers; }
set { _listCustomers = value; OnPropertyChanged("ListCustomers"); }
}
private ICommand _removeCommand;
public ICommand RemoveCommand
{
get { return _removeCommand ?? (_removeCommand = new RelayCommand(param => ButtonRemoveCustomer(), null)); }
set { OnPropertyChanged("ListCustomers"); }
}
private void ButtonRemoveCustomer()
{
ListCustomers.Remove(this.SelectCustomer);
this.SelectCustomer = null;
}
My View
<Intersoft:UXListBox x:Name="CustData" Width="200" MaxHeight="500" SelectedItem="{Binding SelectCustomer, Mode=TwoWay}"
ItemsSource="{Binding ListCustomers, Mode=TwoWay}" ItemTemplate="{StaticResource DataListTemplate}"
VerticalScrollBarVisibility ="Visible" HorizontalScrollBarVisibility="Visible"/>
<Intersoft:UXButton Content="Remove List" Command="{Binding RemoveCommand, Mode=TwoWay}" Width="100"/>
How to fix my code ??
private void ButtonRemoveCustomer()
{
ListCustomers.Remove(this.SelectCustomer);
OnPropertyChanged("ListCustomers");
this.SelectCustomer=null;
}

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.

DataGrid won't update ViewModel

I've got a datagrid which is bound to an ObservableCollection of the ViewModel below. The Datagrid will display all values correctly, so the binding seems to be working, but if I change some value the Grid won't call the setter's of my VM. Could somebody tell me why?
Here's my ViewModel:
public class DocumentVm : ViewModelBase
{
private Document document;
public bool IsNew { get; private set; }
public Document Document {
get { return document; }
}
public DocumentVm(Document document)
{
this.document = document;
IsNew = false;
}
public DocumentVm(Document document, bool isNew)
{
this.document = document;
IsNew = isNew;
}
public String Name
{
get { return document.Name; }
set { document.Name = value; RaisePropertyChangedEvent("Name");}
}
public String Path
{
get { return document.Path; }
set { document.Path = value; }
}
public String Metadata
{
get { return document.Metadata; }
set { document.Metadata = value; }
}
public int SpeechId
{
get { return document.SpeechId; }
set { document.SpeechId = value; }
}
}
Here is the XAML Code:
<DataGrid Margin="3" Grid.Row="7" Grid.Column="1" BorderThickness="0"
ItemsSource="{Binding Path=CurrentSpeech.Documents, Mode=TwoWay}"
SelectedItem="{Binding Path=CurrentSpeech.CurrentDocument, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="MetaDaten" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Metadata, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Pfad" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
Thank you all!
UpdateSourceTrigger=PropertyChanged on the Bindings was missing.
Not sure why the setters aren't getting called but if you use dependency properties they actually do get called. I'm far too drunk to investigate why the CLR properties aren't getting set in your project but this works for me.
public partial class MainWindow : Window
{ public ObservableCollection<DocumentVm> Items
{
get { return (ObservableCollection<DocumentVm>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<DocumentVm>), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<DocumentVm>();
Items.Add(new DocumentVm() { Name = "Name 1"});
Items.Add(new DocumentVm() { Name = "Name 2"});
}
}
public class DocumentVm : DependencyObject
{
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DocumentVm), new PropertyMetadata(null, new PropertyChangedCallback( (s, e)=>
{
// This will run when the property is set.
})));
}

Resources