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; }
}
Related
Well, here is my XAML code:
<ComboBox Grid.Row="2"
Grid.Column="1"
Margin="10"
Width="250"
Height="30"
VerticalAlignment="Top"
HorizontalAlignment="Left"
ItemsSource="{Binding AllProducts}"
SelectedItem="{Binding SelectedProduct}"/>
And this is my C# code:
private ObservableCollection<Product> allProducts = new ObservableCollection<Product>();
public ObservableCollection<Product> AllProducts
{
get => allProducts;
set
{
db.Products.Select(x => x).AsObservableCollection();
RaisePropertyChangedEvent(nameof(AllProducts));
}
}
All I want to do is to select all products from my database and the simply add it to the ComboBox. But somehow it does not work. No error and no exception.
Here is your solution, Make your combobox like this
<ComboBox x:Name="comboBox" Grid.ColumnSpan="2" Grid.Column="1"
HorizontalAlignment="Left" Margin="0,63,0,0" Grid.Row="5" VerticalAlignment="Top" Width="282"
ItemsSource="{Binding AllProducts}"
SelectedValuePath="ID" DisplayMemberPath="Code"
SelectedItem="{Binding SelectedProduct}"/>
then viewmodel take two property like this
private ObservableCollection<AllProducts> _allProducts;
public ObservableCollection<AllProducts> AllProducts
{
get { return _allProducts; }
set
{
_allProducts = value;
base.NotifyOfPropertyChange(nameof(AllProducts));
}
}
private AllProducts _selectedProduct;
public AllProducts SelectedProduct
{
get { return _selectedProduct; }
set
{
_selectedProduct = value;
base.NotifyOfPropertyChange(nameof(SelectedProduct));
}
}
I have made my AllProducts.cs like this
public class AllProducts
{
public long ID { get; set; }
public string Code { get; set; }
}
thats all. here i attached my final output as a screen shot
The issue you have is at your properties set implementation.
private ObservableCollection<Product> allProducts = new ObservableCollection<Product>();
public ObservableCollection<Product> AllProducts
{
// AllProducts-get returns the private field allProducts
get => allProducts;
// AllProducts-set changes the private field allProducts
set
{
this.allProducts = db.Products.Select(x => x).AsObservableCollection();
RaisePropertyChangedEvent(nameof(AllProducts));
}
}
Please do note that you have to update your AllProducts collection if you want to update the contents of the combobox. Changing db.Products will not alter the frontend.
I may also leave some additional tip here, drawing the usage of nameof(AllProducts) obsolete:
void RaisePropertyChangedEvent([System.Runtime.CompilerServices.CallerMemberName] string callee = "")
{
...
}
Adding the CallerMemberName attribute will automatically fill out the callers member name (in the case of AllProducts, "AllProducts"), making copy-paste issues less likely
I think this is true
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
private ObservableCollection<Product> allProducts = new ObservableCollection<Product>();
public ObservableCollection<Product> AllProducts
{
get => allProducts;
set
{
this.allProducts = db.Products.Select(x => x).AsObservableCollection();
RaisePropertyChangedEvent("AllProducts");
}
}
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
I have a problem . I can't change databind when I selecte Item in list view
This My my code xaml ( View ):
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding FCsource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Border Margin="10" Width="440" Height="220" >
<Grid>
<TextBlock Text="{Binding Words, Mode = TwoWay}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
This My my code in ViewModel:
public ObservableCollection _FCsource;
public ObservableCollection FCsource { get { return AddFlashCard(); } set { FCsource = value; OnPropertyChanged(); } }
private Item _SelectedItem;
public Item SelectedItem { get=>_SelectedItem; set
{
_SelectedItem = value;
OnPropertyChanged();
if(_SelectedItem!=null)
{
SelectedItem.Words="hello"
}
}
}
public WordsViewModel()
{
}
private ObservableCollection<Item> AddFlashCard()
{
ObservableCollection<Item> listmn = new ObservableCollection<Item>();
listmn.Add(new Item("qwda");
listmn.Add(new Item("qwda");
listmn.Add(new Item("qwda");
return listmn;
}
With some changes to your code (you have compile errors in there) it works as expected. Predictably, it stops working if there is no INotifyPropertyChanged interface properly implemented on the Item class (specifically signaling changes of the Words property). That is probably what is causing your issue.
Below is the working code (the INotifyPropertyChanged is implemented here using PropertyChanged.Fody version 2.6.0 nuget package):
[AddINotifyPropertyChangedInterface]
public class WordsViewModel
{
public ObservableCollection<WordItem> _FCsource;
public ObservableCollection<WordItem> FCsource { get { return AddFlashCard(); } set { FCsource = value; } }
private WordItem _SelectedItem;
public WordItem SelectedItem
{
get => _SelectedItem; set
{
_SelectedItem = value;
if (_SelectedItem != null)
{
SelectedItem.Words = "hello";
}
}
}
public WordsViewModel()
{
}
private ObservableCollection<WordItem> AddFlashCard()
{
ObservableCollection<WordItem> listmn = new ObservableCollection<WordItem>();
listmn.Add(new WordItem("qwda"));
listmn.Add(new WordItem("qwda"));
listmn.Add(new WordItem("qwda"));
return listmn;
}
}
[AddINotifyPropertyChangedInterface]
public class WordItem
{
public string Words { get; set; }
public WordItem(string words)
{
Words = words;
}
}
If you implemented INotifyPropertyChanged properly and it's still not working, then are you sure that you assigned your View Model to your View's DataContext?
I've read all over the place, that binding is doable in WPF to Interfaces, but I'm having a heck of a time actually getting any traction with it. I'm using EF Core also, if it helps you ready my code. The ComboBox fills with data, so the bind of the data works, but SelectedItem fails to bind, and the text within the selected item shows blank.
I don't get how the following, binds to the object that implements the interface.
The XAML for ComboBox:
<ComboBox Height="23" x:Name="cbJumpList" Width="177" Margin="2" HorizontalAlignment="Left"
IsEditable="False"
DisplayMemberPath="Name"
SelectedItem="{Binding Path=(model:IData.SelectedJumpList), Mode=TwoWay}"
/>
MainWindow.xaml.cs:
protected IData DB { get; private set; }
public MainWindow()
{
InitializeComponent();
DB = new Data.DataSQLite(true);
DB.Bind_JumpLists_ItemsSource(cbJumpList);
}
IData.cs:
public interface IData : IDisposable, INotifyPropertyChanged
{
void Bind_JumpLists_ItemsSource(ItemsControl control);
IJumpList First_JumpList();
IJumpList SelectedJumpList { get; set; } // TwoWay Binding
}
IJumpList.cs
public interface IJumpList
{
long JumpListId { get; set; }
string Name { get; set; }
}
Then within the implemented object (Data.DataSQLite):
public void Bind_JumpLists_ItemsSource(ItemsControl control)
{
control.ItemsSource = null;
db.JumpLists.ToList();
control.ItemsSource = db.JumpLists.Local;
control.Tag = db.JumpLists.Local;
SelectedJumpList = db.JumpLists.FirstOrDefault();
}
public IJumpList SelectedJumpList
{
get { return _SelectedJumpList; }
set
{
_SelectedJumpList = value;
NotifyPropertyChanged();
}
}
IJumpList _SelectedJumpList;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I should add, the PropertyChanged event remains null.
The SelectedItem property of a ComboBox is supposed to be bound to a property and not to a type. For the binding to work you should also set the DataContext of the ComboBox to an instance of the type where this property is defined.
Try this:
<ComboBox Height="23" x:Name="cbJumpList" Width="177" Margin="2" HorizontalAlignment="Left"
IsEditable="False"
DisplayMemberPath="Name"
SelectedItem="{Binding SelectedJumpList}" />
public void Bind_JumpLists_ItemsSource(ItemsControl control)
{
db.JumpLists.ToList();
control.DataContext = this;
control.ItemsSource = db.JumpLists.Local;
control.Tag = db.JumpLists.Local;
SelectedJumpList = db.JumpLists.FirstOrDefault();
}
I am working on a combobox for more than 2 days but did not find the solution.
In one of my question a user named JnJnBoo tried to answer my question and I got some knowledge and code from there.
I am trying to display some data in a combobox in multiple columns using MVVM pattern.
I am using entity framework and SQL Server database.
Here is the code :
namespace ERP_Lite_Trial.ViewModels
{
public class GroupsViewModel : INotifyPropertyChanged
{
public GroupsViewModel()
{
using (DBEntities db = new DBEntities())
{
GroupsAndCorrespondingEffects = (from g in db.Groups
select new GroupAndCorrespondingEffect
{
GroupName = g.Name,
CorrespondingEffect = g.Type_Effect.Name
}
).ToList();
EffectName = (from e in db.Type_Effect
select e.Name).ToList();
}
}
private List<GroupAndCorrespondingEffect> _groupsAndCorrespondingEffects;
public List<GroupAndCorrespondingEffect> GroupsAndCorrespondingEffects
{
get
{
return _groupsAndCorrespondingEffects;
}
set
{
_groupsAndCorrespondingEffects = value;
OnPropertyChanged("GroupsAndCorrespondingEffects");
}
}
private string _selectedGroup;
public string SelectedGroup
{
get
{
return _selectedGroup;
}
set
{
_selectedGroup = value;
OnPropertyChanged("SelectedGroup");
}
}
private List<string> _effectName;
public List<string> EffectName
{
get
{
return _effectName;
}
set
{
_effectName = value;
OnPropertyChanged("EffectName");
}
}
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class GroupAndCorrespondingEffect
{
public string GroupName;
public string CorrespondingEffect;
}
}
And the XAML :
<ComboBox x:Name="cbUnder" ItemsSource="{Binding Path=GroupsAndCorrespondingEffects}"
IsEditable="True" SelectedItem="{Binding Path=SelectedGroup, Mode=TwoWay}"
TextSearch.TextPath="GroupName" Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="3">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=GroupName}"/>
<TextBlock Text="{Binding Path=CorrespondingEffects}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I tried many things but always unsuccessful.
My Combobox is not displaying any data in its items but when I select any Item in the combobox then I get some data in the selectedGroup property. The data is namespace.classname
So I think I need to override the Tostring Method of GroupAndCorrespondingEffect class. But It has got two properties. In which format the databinding in XAML expects the data that is not known to me. So, how to override the tostring method? Or might be I am making some sort of mistake in my code?
Your GroupAndCorrespondingEffect should look like the following
public class GroupAndCorrespondingEffect
{
public string GroupName;
{
get;
set;
}
public string CorrespondingEffect;
{
get;
set;
}
}
And in you XAML
<TextBlock Text="{Binding Path=CorrespondingEffects}"/>
The property name is wrong it contains additional s
so it should be
<TextBlock Text="{Binding Path=CorrespondingEffect}"/>
public class GroupAndCorrespondingEffect
{
public string GroupName;
public string CorrespondingEffect;
}
Make the public variables GroupName and CorrespondingEffect as properties
And in your View model change the type of the property SelectedGroup like below
private GroupAndCorrespondingEffect _selectedGroup;
public GroupAndCorrespondingEffect SelectedGroup
{
get
{
return _selectedGroup;
}
set
{
_selectedGroup = value;
OnPropertyChanged("SelectedGroup");
}
}