fill individual combobox in gridview combocolumn from another event - c#

I have create two Combobox column in gridview. Now i want to fill second combobox depending on value of first combobox (On first combobox selectedValueChanged event). Please reply.

General approach may look like this:
private MyType1 _selectedItem1;
public MyType1 SelectedItem1
{
get { return _selectedItem1; }
set
{
if (_selectedItem1 == value) return;
_selectedItem1 = value;
//replace with string implementation, if needed
OnPropertyChanged(() => SelectedItem1);
if (_selectedItem1 == ...)
{
ItemsSource2 = new List<MyClass2> { ... };
}
else if (_selectedItem1 == ...)
{
...
}
}
}
private IList<MyType2> _itemsSource2;
public IList<MyType2> ItemsSource2
{
get { return _itemsSource2; }
set
{
if (_itemsSource2 == value) return;
_itemsSource2 = value;
OnPropertyChanged(() => ItemsSource2);
}
}

Related

Filter Method of ICollectionView and Properly Binding and ObservableCollection in MVVM

I'm getting stuck into MVVM in WPF and I have setup an ObservableCollection and an ICollectionView. The ICollectionView is set as the ItemsSource of a DataGrid, and the model is a type of Job.
I've setup getters and setter for both of the collections however when I am setting a Filter on the ICollectionView instead of the Job being filtered by the SearchString they're just replicated over and over again, leading me to believe that they way I have the collections setup is totally wrong.
Here is how the two collections are get/set:
public ObservableCollection<Job> AllJobs
{
get
{
foreach (var job in _allJobsList)
_allJobs.Add(job);
return _allJobs;
}
set
{
if (_allJobs == value) return;
OnPropertyChanged("AllJobs");
}
}
public ICollectionView AllJobsView
{
get
{
_allJobsView = CollectionViewSource.GetDefaultView(AllJobs);
return _allJobsView;
}
set
{
if (_allJobsView == value)
{
return;
}
_allJobsView = value;
OnPropertyChanged("AllJobsView");
}
}
Now I have a stringcalled SearchString that is bound to a TextBox.Text. When the text changes I do the following:
public string SearchString
{
get => _searchString;
set
{
if (_searchString == value) return;
_searchString = value;
FilterJobs();
OnPropertyChanged("SearchString");
}
}
private void FilterJobs()
{
AllJobsView.Filter = x =>
{
var viewJob = x as Job;
return viewJob != null && viewJob.Number.Contains(_searchString);
};
}
Now when the page first loads, there are the correct Jobs loaded into the DataGrid. However, as soon as the user types the Jobs are duplicated if the Job.Number does contain the SearchString. How am I able to setup the collections so that I can appropriately set a filter?
The problem is in the getter of your ObservableCollection. Every time you "get" the collection, you are adding every item to the collection all over again.
Your code:
get
{
foreach (var job in _allJobsList)
_allJobs.Add(job);
return _allJobs;
}
Instead, it should be:
get
{
return _allJobs;
}
The setter of your ObservableCollection is also missing the "setter" (private field = value) code:
set
{
if (value != _allJobs)
{
_allJobs = value;
OnPropertyChanged("AllJobs");
}
}
Your Property AllJobs would then be:
private ObservableCollection<Job> _allJobs;
public ObservableCollection<Job> AllJobs
{
get
{
return _allJobs;
}
set
{
if (value != _allJobs)
{
_allJobs = value;
OnPropertyChanged("AllJobs");
}
}
}
The initialization of your collection should be someplace else (and not in the getter of your property), like in the constructor of the ViewModel or/and in a method that a command calls after the user asks for a refresh of the collection.
For example, if your VieModel is called MyViewModel and your List<Job> is called _allJobsList, you can initialize your collection like so:
public MyViewModel()
{
//fill the _allJobsList first, getting from a database for example: _allJobsList = GetJobs();
//and then create an observable collection from that list
AllJobs = new ObservableCollection<Job>(_allJobsList);
}

combobox selection triggers another combobox selection etc. in WPF

I know this has been asked before, but for some reason it seems like its only working for me to an extent. I set up my XAML like this:
<Grid>
<ComboBox x:Name="cbCategories" ItemsSource="{Binding Categories}" DisplayMemberPath="Name" SelectedIndex="{Binding SelectedCategoryIndex}"/>
<ComboBox x:Name="cbSourceParam" ItemsSource="{Binding SourceParameters}" DisplayMemberPath="Name" SelectedIndex="{Binding SelectedSourceParameterIndex}"/>
<ComboBox x:Name="cbTargetParam" ItemsSource="{Binding TargetParameters}" DisplayMemberPath="Name" SelectedIndex="{Binding SelectedTargetParameterIndex}"/>
</Grid>
Then my ViewModel like this:
public class pmCopyParamToParamViewModel : ViewModelBase
{
public pmModel model;
public ObservableCollection<CategoryWrapper> Categories { get; private set; }
public pmCopyParamToParamViewModel(Document doc)
{
this.model = new pmModel(doc);
this.Categories = model.CollectCategories();
SelectedCategoryIndex = 0;
}
private ObservableCollection<ParameterWrapper> _sourceParameters;
public ObservableCollection<ParameterWrapper> SourceParameters
{
get { return _sourceParameters; }
set
{
if (_sourceParameters == value) return;
_sourceParameters = value;
RaisePropertyChanged(() => SourceParameters);
SelectedSourceParameterIndex = 0;
}
}
private ObservableCollection<ParameterWrapper> _targetParameters;
public ObservableCollection<ParameterWrapper> TargetParameters
{
get { return _targetParameters; }
set
{
if (_targetParameters == value) return;
_targetParameters = value;
RaisePropertyChanged(() => TargetParameters);
SelectedTargetParameterIndex = 0;
}
}
// logic for selected category index
private int _selectedCategoryIndex;
public int SelectedCategoryIndex
{
get { return _selectedCategoryIndex; }
set
{
if (_selectedCategoryIndex == value) return;
_selectedCategoryIndex = value;
RaisePropertyChanged(() => SelectedCategoryIndex);
SourceParameters = model.CollectParameters(Categories[SelectedCategoryIndex].ID, new string[] { "String", "Double", "Integer" });
}
}
private int _selectedSourceParameterIndex;
public int SelectedSourceParameterIndex
{
get { return _selectedSourceParameterIndex; }
set
{
if (_selectedSourceParameterIndex == value) return;
_selectedSourceParameterIndex = value;
RaisePropertyChanged(() => SelectedSourceParameterIndex);
TargetParameters = model.CollectParameters(Categories[SelectedCategoryIndex].ID, new string[] { SourceParameters[SelectedSourceParameterIndex].StorageType });
}
}
private int _selectedTargetParameterIndex;
public int SelectedTargetParameterIndex
{
get { return _selectedTargetParameterIndex; }
set
{
if (_selectedTargetParameterIndex == value) return;
_selectedTargetParameterIndex = value;
RaisePropertyChanged(() => SelectedTargetParameterIndex);
}
}
}
What I was expecting to happen is that on ViewModel initilization, it would collect all Categories. I then call SelectedCategoryIndex and set it to 0. That in turn should trigger SourceParameters to update and set the selected item initially to 0. That in turn would trigger TargetParameters to trigger and set the initial SelectedParameterIndex to 0.
So far, I am only seeing the Categories and Source Parameters getting set, but the Target Paramters combobox doesn't get set until i manually touch/change selection for the source parameters combobox.
Did I miss something here? Thanks!
Could this be the issue?
private int _selectedSourceParameterIndex; // Starts off as 0
public int SelectedSourceParameterIndex
{
set
{
// Setting to zero would not change the value, and this will return
if (_selectedSourceParameterIndex == value) return;
//... nothing here gets executed ...
_selectedSourceParameterIndex = value;
RaisePropertyChanged(() => SelectedSourceParameterIndex);
TargetParameters = model.CollectParameters(Categories[SelectedCategoryIndex].ID, new string[] { SourceParameters[SelectedSourceParameterIndex].StorageType });
}
}
I personally prefer binding SelectedItem instead of SelectedIndex. It'll give you the actual object (or null if none is selected) so you don't have to deal with the intricacies of a combobox/list indexing.

Adding new item to ListView

I have a ListView and a button AddNewItem and two forms both wrapped in two stack panels: spanel1 and spanel2
when I select an item from the list, spanel1 shows data of the selected item
when I click on the button the form of spanel2 is empty
in the view model I have two properties: selectedItem and newItem
Problem:
if I fill data of the new item in the form of spanel2 and click Save (see Save command code) the NewItem property will not be changed
private void SaveItem(object obj)
{
if (this.selectedItem != null)
{
this.item.EditAsync(this.selectedItem );
}
else
{
this.ietm.InsertAsync(this.NewItem);
}
this.LoadData();
}
private Item newItem;
public Item NewItem
{
get
{
return this.newItem ?? new Item();
}
set
{
this.newItem = value;
this.RaisePropertyChanged("NewItem");
}
}
private Item selectedItem;
public Item SelectedItem
{
get
{
return this.selectedItem;
}
set
{
this.selectedItem= value;
this.RaisePropertyChanged("SelectedItem");
}
}
what am I doing wrong?
Thanks.
1) create a new button in XAML
2) create a new command in UserVM class(call it UpdateOrAddNew command) and associate it with the button using "Command" property.
See this for more information: How to bind WPF button to a command in ViewModelBase?
3) In your UpdateOrAddNew command handler, check if SelectedUser is null(then create new user) or if it's not null, update existing one.
To take car of the TextBox issue, you need to create a wrapper that holds always information.
Add this to your UserVM:
public User UserRecord
{
get
{
if(userRecord == null)
return userRecord = new User();
}
set
{
userRecord = value;
onPropertyChanged("UserRecord");
}
}
now you need to modify your TextBoxes to bind against UserRecord, instead of SelectedUser.
and modify your SelectedUser:
public User SelectedUser
{
get { return selecteduser; }
set
{
selecteduser = value;
onPropertyChanged("SelectedUser");
UserRecord = selecteduser;
}
}

Update ListcollectionView based on current item of other ListCollectionView

I want to Update ListCollectionView in a listbox each time the Item of another ListCollection gets selected.
I have 2 ListViewCollection, SceneCollectionView and ShotCollectionView. I want to have the SceneCollection filtered based on a property SceneNumber in ShotCollectionView, but I can get the ShotCollectionView to update when I go from one item to the other in SceneCollectionView.
This is my ViewModel
public class ShotListViewModel : NotifyUIBase
{
public ListCollectionView SceneCollectionView { get; set; }
private Scenes CurrentScene
{
get { return SceneCollectionView.CurrentItem as Scenes; }
set { SceneCollectionView.MoveCurrentTo(value); RaisePropertyChanged(); }
}
private ObservableCollection<Shot> _allShots = new ObservableCollection<Shot>();
public ObservableCollection<Shot> AllShots
{
get { return _allShots; }
set { _allShots = value; RaisePropertyChanged();}
}
private ListCollectionView _allShotsCollection;
public ListCollectionView AllShotsCollection
{
get
{
if (_allShotsCollection == null)
{
_allShotsCollection = new ListCollectionView(this.AllShots);
_allShotsCollection.Filter = IsSceneNumber;
}
return _allShotsCollection;
}
}
private bool IsSceneNumber(object obj)
{
if (obj as Shot != null
&& (obj as Shot).SceneNumber == (SceneCollectionView.CurrentItem as Scene).SceneNumber)
{
return true;
}
return false;
}
public ShotListViewModel()
{
SceneCollectionView = Application.Current.Resources["SceneCollectionView"] as ListCollectionView;
GetShotList(); //Populates the AllShots Observable collection.
AddShotCommand = new RelayCommand(AddShot);
FilterShotsCommand = new RelayCommand(AddShot);
}
What am I missing here to make it work or is it better to use ICollectionViewLiveShaping. but I have no idea how to implement that
I don`t understand what you tried to do, but lets talk on an example :
Lets say we have
ListBox1 binded to ListBox1Items and
ListBox2 binded to ListBox2Items.
if you want to filter the data from ListBox2 you have to filter ListBox2Items. How to do that ? Is simple : ListBox1 has a property SelectedItem which you can bind to --- lets say --- ListBox1SelectedItem. Every time when selection change, in the setter of the ListBox1SelectedItem you can trigger a filter on ListBox2Items.
Hope you understand what I`ve explained.

StackOverflowException when trying to assign public value to private backing field

I have a public property with private backer like the one below. On the line selected = Selected; I get an SO exception. It would appear that assignment is causing some infinite recursion. Can someone give a more detailed explanation of what is happening? What should the code be instead?
//other class stuff AssetTypes is an enum btw.
private AssetTypes? selected = null;
public AssetTypes? Selected
{
get
{
return selected;
}
set
{
selected = Selected;
if (selected == AssetTypes.Image)
{
image.Click();
}
else if (selected == AssetTypes.Video)
{
video.Click();
}
else
{
selected = null;
}
}
}
The only part that can be changed is the assignment in the setter. The if-else logic needs to be untouched.
A setter has a parameter called value, which is the value that the property is being set to. You should get the new value from that, not from Selected, which just calls the getter, which returns the previous value.
Hence, this line does nothing:
selected = Selected;
And you should never assign to Selected from within its own setter, because that calls the same setter, hence your infinite recursion.
Try this instead:
set
{
if (value == AssetTypes.Image)
{
image.Click();
selected = value;
}
else if (value == AssetTypes.Video)
{
video.Click();
selected = value;
}
else
{
selected = null;
}
}
I think the problem is in the else: Selected = null
Use selected = null with the lowercase s.
Greetings!
You have an infinite recursion on Selected = null, that's why you get a stackoverflow exception. It calls over and over Selected set until it finally blows up with an exception. Change it by
selected = null;
selected = Selected does nothing
what is passed to Set is value;
public AssetTypes? Selected
{
get
{
return selected;
}
set
{
if (selected == value) return; // use this to not do it again
selected = value;
if (selected == AssetTypes.Image)
{
image.Click();
}
else if (selected == AssetTypes.Video)
{
video.Click();
}
else
{
selected = null; // Selected = null; was the recursion
}
NotifyPropertyChanged("Selected"); // this optional and only if you implement INPC
}
}

Categories