I have an issue on binding a collection into my SfCombobox when the data comes from an async call ( my api ). Of course, all works correctly with a local list.
I already check the data coming from my api and the binding with my viewmodel property all is working.
Here the XAML sample :
<border:SfBorder
Grid.Row="4"
BorderColor="{Binding Source={x:Reference CategoryCombo}, Path=IsFocused, Converter={StaticResource ColorConverter}, ConverterParameter=0}"
Style="{StaticResource SfBorderStyle}">
<combobox:SfComboBox
AllowFiltering="True"
x:Name="CategoryCombo"
Style="{StaticResource ComboBoxStyle}"
DataSource="{Binding Categories}" DisplayMemberPath="Name" SelectedValuePath="Id"
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"/>
</border:SfBorder>
and the full view model
[Preserve(AllMembers = true)]
[DataContract]
public class SearchPageViewModel: BaseViewModel
{
private ObservableCollection<Category> categories;
private readonly ICategoryQueriesServices categoryQueriesServices;
public ObservableCollection<Category> Categories
{
get { return this.categories; }
set
{
if (this.categories == value)
{
return;
}
this.categories = value;
this.NotifyPropertyChanged();
}
}
private Category selectedCategory;
public Category SelectedCategory
{
get
{
return selectedCategory;
}
set
{
if (selectedCategory != value)
{
selectedCategory = value;
this.NotifyPropertyChanged();
}
}
}
public SearchPageViewModel(ICategoryQueriesServices categoryQueriesServices)
{
this.categoryQueriesServices = categoryQueriesServices;
}
protected override void CurrentPageOnAppearing(object sender, EventArgs eventArgs)
{
base.CurrentPageOnAppearing(sender, eventArgs);
Task.Run(async () =>
{
var categories = await this.categoryQueriesServices.GetCategoryModelsAsync();
Device.BeginInvokeOnMainThread(() =>
{
Categories = new ObservableCollection<Category>(categories);
});
});
}
}
thanks for your help
Related
I have a code for checkbox. Please tell me how to write it in MVVM?
There is a function that I can choose only one checkbox. In general I understand that I must to write command.
XAML:
<StackLayout>
<!-- Place new controls here -->
<ListView ItemsSource="{Binding Items}" ItemSelected="ListView_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<CheckBox HorizontalOptions="Start" Color="Black" CheckedChanged="CheckBox_CheckedChanged"
IsChecked="{Binding IsSelected}"
/>
<Label Text="meow" TextColor="Gray"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
CODE BEHIND
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel();
}
Model previousModel;
private void CheckBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
if (previousModel != null)
{
previousModel.IsSelected = false;
}
Model currentModel = ((CheckBox)sender).BindingContext as Model;
previousModel = currentModel;
if (currentModel.IsSelected)
{
var viewModel = BindingContext as MainPageViewModel;
int index = viewModel.Items.IndexOf(currentModel);
}
}
private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (previousModel != null)
{
previousModel.IsSelected = false;
}
Model currentModel = e.SelectedItem as Model;
currentModel.IsSelected = true;
previousModel = currentModel;
}
}
ViewModel
public class MainPageViewModel
{
public List<Model> Items { set; get; }
public MainPageViewModel()
{
List<Model> list = new List<Model>();
for (int i=0; i<10; i++)
{
list.Add(new Model { IsSelected = false });
}
Items = list;
}
}
Model
public class Model : INotifyPropertyChanged
{
bool isSelected;
public bool IsSelected
{
set
{
isSelected = value;
onPropertyChanged();
}
get => isSelected;
}
public event PropertyChangedEventHandler PropertyChanged;
void onPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
For an event to command use Corcav.Behavior nuget
https://github.com/corradocavalli/Corcav.Behaviors
...
xmlns:corcav="clr-namespace:Corcav.Behaviors;assembly=Corcav.Behaviors"
...
<CheckBox>
<corcav:Interaction.Behaviors>
<corcav:BehaviorCollection>
<corcav:EventToCommand EventName="CheckedChanged" Command="{Binding Path=CheckBoxChangedCommand}" Commandparameter="{Binding .}"/>
</corcav:BehaviorCollection>
</corcav:Interaction.Behaviors>
</CheckBox>
Add this command in ViewModel and write your logic
public ICommand CheckBoxChangedCommand{ get; set; }
...
CheckBoxChangedCommand= new Command<object>(CheckBoxChanged);
...
private void CheckBoxChanged(object obj)
{
//set all list/collection element to false with linq
if(obj is Model model)
{
model.IsSelected = true;
}
}
For now, CheckBox do not support Command. This issue has reported on Github and have not fixed. We could follow this enhancement. https://github.com/xamarin/Xamarin.Forms/issues/6606
You could use the InputKit instead. Install Xamarin.Forms.InputKit on NuGet.
It provides CheckChangedCommand.
CheckChangedCommand: (Command) Bindable Command, executed when check changed.
<input:CheckBox HorizontalOptions="Start" Color="Black" CheckChangedCommand="{Binding CheckBoxChangedCommand}">
I have a ViewModel with all the properties that i will need in every sub ViewModel.
It's the first time i try to split commands and viewmodel to multiple files. Last time everything was in the same ViewModel and it was a pain to work with it. Everything shows up as expected but i want to find a way to pass the same data in every viewmodel.
From my GetOrdersCommand, i want to get the HeaderViewModel.SelectedSource property. I didn't find any way to do it without getting a null return or loosing the property data...
I would like to call my GetOrdersCommand from HeaderView button too.
Any tips how i can achieve this ? Perhaps, my design is not good for what i'm trying to do ?
MainWindow.xaml
<views:HeaderView Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" DataContext="{Binding HeaderViewModel}" LoadHeaderViewCommand="{Binding LoadHeaderViewCommand}"/>
<TabControl TabStripPlacement="Bottom" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2">
<TabItem Header="General">
</TabItem>
<TabItem Header="Orders">
<views:OrderView DataContext="{Binding OrderViewModel}" GetOrdersCommand="{Binding GetOrdersCommand}"/>
</TabItem>
</TabControl>
HeaderView.xaml
<DockPanel>
<ComboBox DockPanel.Dock="Left" Width="120" Margin="4" VerticalContentAlignment="Center" ItemsSource="{Binding SourceList}" SelectedItem="{Binding SelectedSource}" DisplayMemberPath="SourceName"/>
<Button x:Name="btnTest" HorizontalAlignment="Left" DockPanel.Dock="Left" Margin="4" Content="Test"/>
</DockPanel>
HeaderView.xaml.cs
public partial class OrderView : UserControl
{
public ICommand GetOrdersCommand
{
get { return (ICommand)GetValue(GetOrdersCommandProperty); }
set { SetValue(GetOrdersCommandProperty, value); }
}
public static readonly DependencyProperty GetOrdersCommandProperty =
DependencyProperty.Register("GetOrdersCommand", typeof(ICommand), typeof(OrderView), new PropertyMetadata(null));
public OrderView()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (GetOrdersCommand != null)
{
GetOrdersCommand.Execute(this);
}
}
}
MainViewModel.cs
private OrderViewModel orderViewModel;
public OrderViewModel OrderViewModel { get; set; } // Getter, setter with OnPropertyChanged
private HeaderViewModel headerViewModel;
public HeaderViewModel HeaderViewModel { get; set; } // Getter, setter with OnPropertyChanged
public MainViewModel()
{
HeaderViewModel = new HeaderViewModel();
OrderViewModel = new OrderViewModel();
}
HeaderViewModel.cs
public ICommand LoadHeaderViewCommand { get; set; }
public HeaderViewModel()
{
LoadHeaderViewCommand = new LoadHeaderViewCommand(this);
}
GetOrdersCommand.cs
public class GetOrdersCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly OrderViewModel _orderViewModel;
public GetOrdersCommand(OrderViewModel orderViewModel)
{
_orderViewModel = orderViewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
/* Build Order List according to HeaderViewModel.SelectedSource */
_orderViewModel.Orders = new ObservableCollection<Order>()
{
new Order { ID = 1, IsReleased = false, Name = "Test1"},
new Order { ID = 2, IsReleased = true, Name = "Test2"},
};
}
}
Thanks guys ! I moved my commands to their owning ViewModel as suggested.
I tried MVVVM Light Tools and found about Messenger Class.
I used it to send my SelectedSource (Combobox from HeaderView) from HeaderViewModel to OrderViewModel. Am i suppose to use Messenger class like that ? I don't know, but it did the trick!!!
I thought about moving GetOrdersCommand to OrderViewModel, binding my button command to OrderViewModel, binding SelectedSource as CommandParameter but i didn't know how i was suppose to RaiseCanExecuteChanged when HeaderViewModel.SelectedSource changed... Any advice?
MainWindow.xaml
<views:HeaderView DataContext="{Binding Source={StaticResource Locator}, Path=HeaderVM}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"/>
<TabControl TabStripPlacement="Bottom" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="2">
<TabItem Header="General">
</TabItem>
<TabItem Header="Orders">
<views:OrderView DataContext="{Binding Source={StaticResource Locator}, Path=OrderVM}"/>
</TabItem>
</TabControl>
OrderViewModel.cs
private ObservableCollection<Order> _orders;
public ObservableCollection<Order> Orders
{
get { return _orders; }
set
{
if (_orders != value)
{
_orders = value;
RaisePropertyChanged(nameof(Orders));
}
}
}
public OrderViewModel()
{
Messenger.Default.Register<Source>(this, source => GetOrders(source));
}
private void GetOrders(Source source)
{
if (source.SourceName == "Production")
{
Orders = new ObservableCollection<Order>(){
new Order { ID = 1, IsReleased = false, Name = "Production 1" }
};
}
else
{
Orders = new ObservableCollection<Order>(){
new Order { ID = 2, IsReleased = true, Name = "Test 1" }
};
}
}
Part of HeaderViewModel.cs
private Source _SelectedSource;
public Source SelectedSource
{
get { return _SelectedSource; }
set
{
if (_SelectedSource != value)
{
_SelectedSource = value;
RaisePropertyChanged(nameof(SelectedSource));
GetOrdersCommand.RaiseCanExecuteChanged();
}
}
}
private RelayCommand _GetOrdersCommand;
public RelayCommand GetOrdersCommand
{
get
{
if (_GetOrdersCommand == null)
{
_GetOrdersCommand = new RelayCommand(GetOrders_Execute, GetOrders_CanExecute);
}
return _GetOrdersCommand;
}
}
private void GetOrders_Execute()
{
Messenger.Default.Send(SelectedSource);
}
private bool GetOrders_CanExecute()
{
return SelectedSource != null ? true : false;
}
I have combo box with some contents. I want it to get reset to the first item when a button is clicked.
WPF and C#.net is used.
<ComboBox x:Name="categoriesComboBox" Height="40" Grid.Row="0" ItemsSource="{Binding Categories}" SelectedValue="{Binding SelectedCategory}"
VerticalContentAlignment="Center" Background="{StaticResource InventoryManagementAlternatingRowBackground}" BorderBrush="Transparent">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryName}" FontSize="20" Margin="3,0" FontFamily="Malgun Gothic" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
private void ClearFilter_Click(object sender, RoutedEventArgs e)
{
SearchFiltersControl s = new SearchFiltersControl();
s.ResetCategoryComboBox();
}
public void ResetCategoryComboBox()
{
categoriesComboBox.SelectedIndex = -1;
}
I am adding the ViewModel. Add I want to how to use this View Model to reset the ComboBox:
using System;
using System.Collections.ObjectModel;
namespace NextGen.Optik.UI.Presentation.ViewModels.Inventory.Contracts
{
public class SearchFilterCategoryViewModel : ViewModelBase
{
private int _categoryId;
public int CategoryId
{
get => _categoryId;
set
{
if (_categoryId != value)
{
_categoryId = value;
RaisePropertyChangedEvent();
}
}
}
private string _categoryName;
public string CategoryName
{
get => _categoryName;
set
{
if (_categoryName != value)
{
_categoryName = value;
RaisePropertyChangedEvent();
}
}
}
private ObservableCollection<SearchFilterViewModel> _filters = new ObservableCollection<SearchFilterViewModel>();
public ObservableCollection<SearchFilterViewModel> Filters
{
get => _filters;
set
{
if (_filters != value)
{
_filters = value;
RaisePropertyChangedEvent();
}
}
}
}
}
Expected Result: When the reset button is clicked, first item has to be selected.
Actual Results: Not getting reset.
I want to do the following:
I have a combobox that is bound to the ComputerNames property. Once I select a new computer
the XAML will do everything for me and the new SelectedComputer is set.
This is the point I'm not sure how to continue. So every time a new computer is selected I want the
CustomerNames property to change to (and subsequently the CustomerNames combobox). The CustomerNames
are dependent on the SelectedComputer property. How and where do I put the logic to give the CustomerComboBox
the items it needs?
MainWindow.xaml.cs
public partial class MainWindow : Window
{
MainViewModel _main = new MainViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _main;
}
}
XAML:
<Window.Resources>
<vm:MainViewModel x:Key="viewModel" />
</Window.Resources>
<Border Margin="10">
<Grid>
<!-- Comboboxes -->
<GroupBox Header="Computer" Grid.Column="0" Grid.ColumnSpan="4" Margin="0 0 4 4">
<ComboBox ItemsSource="{Binding ComputerNames}"
SelectedItem="{Binding SelectedComputer}"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0"/>
</GroupBox>
<GroupBox Header="Customer" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" Margin="0 0 4 4" >
<ComboBox ItemsSource="{Binding CustomerNames}"
SelectedItem="{Binding SelectedCustomer}"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0"/>
</GroupBox>
<!-- Main list -->
<DataGrid x:Name="dataGrid" Grid.Row="2" Grid.ColumnSpan="8"
ItemsSource="{Binding Path=Services, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
AutoGenerateColumns="False"
IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="*" Binding="{Binding DisplayName}"/>
<DataGridTextColumn Header="Status" Width="*" Binding="{Binding Status}" />
<DataGridTextColumn Header="Machine Name" Width="*" Binding="{Binding MachineName}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Border>
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
#region INotify methods
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion
/*---------------------------- C U S T O M E R S -----------------------------*/
#region Customer Properties
private string _selectedCustomer;
private ObservableCollection<string> _customerNames;
public string SelectedCustomer
{
get => _selectedCustomer;
set
{
SetField(ref _selectedCustomer, value);
Services = Utils.UpdatedServices(SelectedComputer, SelectedCustomer);
}
}
public ObservableCollection<string> CustomerNames
{
get => _customerNames;
set
{
SetField(ref _customerNames, value);
Services = Utils.UpdatedServices(SelectedComputer, SelectedCustomer);
}
}
#endregion
/*---------------------------- S E R V I C E S -----------------------------*/
#region Services Properties
private ObservableCollection<ServiceController> _services;
private ObservableCollection<ServiceController> _selectedServices;
public ObservableCollection<ServiceController> SelectedServices
{
get => _selectedServices;
set => SetField(ref _selectedServices, value);
}
public ObservableCollection<ServiceController> Services
{
get => _services;
set => SetField(ref _services, value);
}
#endregion
/*---------------------------- C O M P U T E R S -----------------------------*/
#region Computer Properties
private string _selectedComputer;
private ObservableCollection<string> _computerNames;
public string SelectedComputer
{
get => _selectedComputer;
set
{
SetField(ref _selectedComputer, value);
CustomerNames = Utils.UpdatedCustomerNames(SelectedComputer);
}
}
public ObservableCollection<string> ComputerNames
{
get => _computerNames;
set => SetField(ref _computerNames, value);
}
#endregion
/*---------------------------- C O M M A N D S -----------------------------*/
#region Commands
public StartServiceCommand StartServiceCommand { get; set; }
public void StartService(ObservableCollection<ServiceController> serviceControllers)
{
try
{
foreach(var service in serviceControllers)
if (service.Status != ServiceControllerStatus.Running)
service.Start();
RefreshServices();
}
catch (Exception ex) { }
}
public StopServiceCommand StopServiceCommand { get; set; }
public void StopService(ObservableCollection<ServiceController> serviceControllers)
{
try
{
foreach (var service in serviceControllers)
if (service.Status != ServiceControllerStatus.Stopped &&
service.Status != ServiceControllerStatus.StopPending)
service.Stop();
RefreshServices();
}
catch (Exception ex) { }
}
public RefreshServicesCommand RefreshServicesCommand { get; set; }
public void RefreshServices()
{
Services = Utils.UpdatedServices(SelectedComputer, SelectedCustomer);
}
#endregion
public MainViewModel()
{
#region Initialize
_services = new ObservableCollection<ServiceController>();
_computerNames = new ObservableCollection<string>();
_customerNames = new ObservableCollection<string>();
_selectedComputer = _customerNames.FirstOrDefault();
_selectedServices = new ObservableCollection<ServiceController>();
ComputerNames = new ObservableCollection<string> { Environment.MachineName, Environment.MachineName };
StartServiceCommand = new StartServiceCommand(this);
StopServiceCommand = new StopServiceCommand(this);
RefreshServicesCommand = new RefreshServicesCommand(this);
#endregion
}
}
So every time a new computer is selected I want the CustomerNames property to change to (and subsequently the CustomerNames combobox). The CustomerNames are dependent on the SelectedComputer property. How and where do I put the logic to give the CustomerComboBox the items it needs?
For example in the setter of the SelectedComputer property in the view model:
public string SelectedComputer
{
get => _selectedComputer;
set
{
SetField(ref _selectedComputer, value);
//set the CustomerNames property here...
CustomerNames = ?
}
}
It's in the view model that you should implement your application logic, like for example what happens when you click on a button or select an item in a ComboBox.
I have two ComboBox in the View. I want to change the second one itemsources when the first one selecteditem is changed:
The View:
<ComboBox ItemsSource="{Binding ProductsList}" SelectedValue="{Binding SelectedProduct}" DisplayMemberPath="ChipName"/>
The ViewModel:
public MainViewModel()
{
GetProductsList();
}
ProductDb pd = new ProductDb();
public ObservableCollection<Product> ProductsList { get; set; }
private void GetProductsList()
{
try
{
ProductsList = new ObservableCollection<Product>(pd.GetProducts());
}
catch(Exception e) { Console.WriteLine(e.ToString()); }
if (ProductsList != null) SelectedProduct = ProductsList[0];
}
The Model read data from xml:
public class ProductDb
{
public ObservableCollection<Product> GetProducts()
{
ObservableCollection<Product> _products = new ObservableCollection<Product>();
XDocument doc = XDocument.Load(#".\Config\Chip.xml");
foreach(XElement productRow in doc.Root.Elements("Chip"))
{
var p = new Product(productRow.Element("ChipName").Value, productRow.Element("Series").Value, productRow.Element("Type").Value,
Convert.ToUInt32(productRow.Element("FlashAddress").Value), Convert.ToUInt32(productRow.Element("PageCount").Value), Convert.ToUInt32(productRow.Element("PageSize").Value),
Convert.ToUInt32(productRow.Element("RAMAdd").Value, 16), Convert.ToUInt32(productRow.Element("RAMLength").Value, 16), productRow.Element("Crystals").Value);
foreach (XElement crystal in productRow.Element("Crystals").Elements())
{
p.Crystals.Add(crystal.Value);
}
_products.Add(p);
}
return _products;
}
}
Now the above code populate the ChipName in the first combobox, I want to display the Craystal of the SelectedProdu in the sencond combobox. how should I do? Thanks in advance!
---Update:---
Crystals is an element of Product. It contains several Crystal. The xml file looks like this:
<System>
<Chip>
<ChipName>Hxxxxxxx</ChipName>
<Series>CM0+</Series>
<Type>0</Type>
<FlashAddress>00000000</FlashAddress>
<PageCount>256</PageCount>
<PageSize>512</PageSize>
<RAMAdd>20000000</RAMAdd>
<RAMLength>0x1800</RAMLength>
<Crystals>
<Crystal>4</Crystal>
<Crystal>6</Crystal>
<Crystal>8</Crystal>
<Crystal>10</Crystal>
<Crystal>12</Crystal>
<Crystal>16</Crystal>
<Crystal>18</Crystal>
<Crystal>20</Crystal>
<Crystal>24</Crystal>
<Crystal>32</Crystal>
</Crystals>
</Chip>
</System>
This should work provided that you implement the INotifyPropertyChanged interface and raise the PropertyChanged event for the SelectedProduct property:
<ComboBox ItemsSource="{Binding SelectedProduct.Crystals}" />
This should also work provided that Crystals is a public property of the Product class:
<ComboBox x:Name="a" ItemsSource="{Binding ProductsList}"
SelectedValue="{Binding SelectedProduct}"
DisplayMemberPath="ChipName"/>
<ComboBox x:Name="b" ItemsSource="{Binding SelectedItem.Crystals, ElementName=a}" />
Somthing like what you have already for products, but need to tell UI where to find the new item source for the new combo:
XAML:
<ComboBox ItemsSource="{Binding Crystals}"
SelectedValue="{Binding SelectedCrystal}"
DisplayMemberPath="CrystalName"/>
C#:
public Product SelectedProduct
{
set
{
// your private member setting...
// raise property change for crystal collection for UI to respond
}
}
public Product SelectedCrystal
{
set
{
// your private member setting...
// raise property change for crystal collection for UI to respond
}
}
public ObservableCollection<Crystal> Crystals
{
get
{
if (SelectedProduct != null)
return SelectedProduct.Crystals;
return new ObservableCollection<Crystal>();
}
}
If you like, you can also play with either visibility or enabled state of the crystal control based on whether there are any valid objects in its bound collection...
xaml:
<ComboBox ItemsSource="{Binding ProductsList}" SelectedItem="{Binding SelectedProduct,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="ChipName"/>
<ComboBox ItemsSource="{Binding Crystals}" />
c#:
public class MainModel:INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
ObservableCollection<Product> _ProductsList = null;
ObservableCollection<Crystal> Crystals = null;
Product _SelectedProduct = null;
public ObservableCollection<Product> ProductsList
{
get
{
return _ProductsList;
}
set
{
_ProductsList=value;
PropertyChanged?.Invoke(this,new System.ComponentModel.PropertyChangedEventArgs("ProductsList"));
}
}
public ObservableCollection<Crystal> Crystals
{
get
{
return _Crystals;
}
set
{
_Crystals=value;
PropertyChanged?.Invoke(this,new System.ComponentModel.PropertyChangedEventArgs("Crystals"));
}
}
public Product SelectedProduct
{
get
{
return _SelectedProduct;
}
set
{
_SelectedProduct = value;
PropertyChanged?.Invoke(this,new System.ComponentModel.PropertyChangedEventArgs("SelectedProduct"));
ResetCrystals();
}
}
private void ResetCrystals()
{
Crystals=.....
}
}
did you tried something like this:
<StackPanel>
<ComboBox Width="100" Height="22" ItemsSource="{Binding ProductsList}" SelectedValue="{Binding SelectedProduct}" DisplayMemberPath="ChipName"/>
<ComboBox Width="100" Height="22" ItemsSource="{Binding SelectedProduct.Crystals}" DisplayMemberPath="Value"/>
</StackPanel>
For me, this worked well, i just set Product.Crystals as ObservableCollection, and did SelectedProduct with INotifyPropertyChanged interface
As example:
View Model:
public ObservableCollection<Product> ProductsList { set; get; }
public Product SelectedProduct
{
set
{
_selectedProduct = value;
NotifyPropertyChanged();
}
get
{
return _selectedProduct;
}
}
Model:
public class Product
{
public ObservableCollection<Crystal> Crystals { set; get; }
public String ChipName { set; get; }
public Product(ObservableCollection<Crystal> l, String ChipName)
{
this.ChipName = ChipName;
Crystals = l;
}
}
public class Crystal
{
public string Value { set; get; }
}