Adding products to Combobox ViewModel C# - c#

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");
}
}

Related

Change combobox items when another combobox selecteditem changed?

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; }
}

Show properties of selected items in ListView in DataGrid in WPF application

Screenshot:
Selecting items from above ListView shows their properties in the datagrid below.
XAML:
<Window x:Class="EmployeeSystem.Run_with_XML"
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:EmployeeSystem"
mc:Ignorable="d"
Title="Run with XML" Height="398.299" Width="305.91" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
<Grid Height="325" VerticalAlignment="Top">
<StackPanel>
<ListView Height="100" DisplayMemberPath="Name" ItemsSource="{Binding employees}" SelectedItem="{Binding SelectedEmployee}"/>
<DataGrid Height="100" ItemsSource="{Binding EmployeesView}"/>
</StackPanel>
<Button x:Name="button" Content="Create New" Height="24" Margin="112,0,111,-30" VerticalAlignment="Bottom"/>
</Grid>
</Window>
VeiwModel:
class ViewModel
{
private Employee selectedEmployee;
public ICollectionView EmployeesView { get; set; }
EmployeeManagerXML emx = new EmployeeManagerXML();
public ViewModel()
{
EmployeesView = new ListCollectionView(emx.getEmployeesList()) //this is an ObservableCollection
{
Filter = obj =>
{
var Employee = (Employee)obj;
return SelectedEmployee != null && Employee.Name == SelectedEmployee.Name;
}
};
}
public Employee SelectedEmployee
{
get { return selectedEmployee; }
set
{
if (selectedEmployee != value)
{
selectedEmployee = value;
EmployeesView.Refresh();
}
}
}
}
I have been desperately trying to achieve this for days now...i searched google for hours...tried all the related pages on stackoverflow...This post is my last hope...someone please just show me where am going wrong with this. I already know this is a duplicate..This scenario is exactly the same as mine and I did everything exactly the same as that answer says...still mine doesn't work.
You should impliment INotifyPropertyChanged interface to reflect changes in View
for example below,
public class ViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
EmployeeManagerXML emx = new EmployeeManagerXML();
public ViewModel()
{
}
private Void RefreshEmployeeList()
{
EmployeesView = new ListCollectionView(emx.getEmployeesList()) //this is an ObservableCollection
{
Filter = obj =>
{
var Employee = (Employee)obj;
return SelectedEmployee != null && Employee.Name == SelectedEmployee.Name;
}
};
}
private Employee selectedEmployee;
private ICollectionView _EmployeesView;
public ICollectionView EmployeesView
{
get { return _EmployeesView; }
set
{
_EmployeesView = value;
NotifyPropertyChanged();
}
}
public Employee SelectedEmployee
{
get { return selectedEmployee; }
set
{
if (selectedEmployee != value)
{
selectedEmployee = value;
NotifyPropertyChanged();
RefreshEmployeeList();
}
}
}
MSDN
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.

Combobox does not show anything in its items but when I select any items I get some data

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");
}
}

EASY way to refresh ListBox in WPF?

I have created a simple form that inserts/updates/deletes a values for Northwind Customers.
Everything works fine, except in order to see a results, I have to close it, and reopen again.
My form looks like this :
I've searched tens of articles on how to refresh ListBox, but all of those use interface implementing, or using DataSets, and stuff I have never heard of and cannot implement. It's a very simple project, using simple procedures. Is there an easy way to refresh the list of customers without adding many lines of code?
The simple answer is: myListBox.Items.Refresh();
Are you using ObservableCollection and does your model implement INotifyPropertyChanged these two things will automaticly update the ListBox on any change. no need to explicitly refresh the list.
Here is a small example of using ObservableCollection and INotifyPropertyChanged, obviously you will populate your ObservableCollection from your SQL database.
Window:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<MyModel> _list = new ObservableCollection<MyModel>();
private MyModel _selectedModel;
public MainWindow()
{
InitializeComponent();
List.Add(new MyModel { Name = "James", CompanyName = "StackOverflow"});
List.Add(new MyModel { Name = "Adam", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Chris", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Steve", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Brent", CompanyName = "StackOverflow" });
}
public ObservableCollection<MyModel> List
{
get { return _list; }
set { _list = value; }
}
public MyModel SelectedModel
{
get { return _selectedModel; }
set { _selectedModel = value; NotifyPropertyChanged("SelectedModel"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml
<Window x:Class="WpfApplication11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Name="UI">
<Grid>
<ListBox ItemsSource="{Binding ElementName=UI, Path=List}" SelectedItem="{Binding ElementName=UI, Path=SelectedModel}" Margin="0,0,200,0" DisplayMemberPath="DisplayMember" SelectedIndex="0" />
<StackPanel HorizontalAlignment="Left" Height="100" Margin="322,10,0,0" VerticalAlignment="Top" Width="185">
<TextBlock Text="Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Company Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.CompanyName, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Window>
Model
public class MyModel : INotifyPropertyChanged
{
private string _name;
private string _companyName;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public string CompanyName
{
get { return _companyName; }
set { _companyName = value; NotifyPropertyChanged("CompanyName"); }
}
public string DisplayMember
{
get { return string.Format("{0} ({1})", Name, CompanyName); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
PropertyChanged(this, new PropertyChangedEventArgs("DisplayMember"));
}
}
}
In this case any edit to properties will Update your list instantly, also will update when new Items are added/removed.
How about calling ListBox.UpdateLayout?
Of course you also need to update the particular item(s) so that it returns the updated string from the ToString method.
UPDATE: I think you also need to call ListBox.InvalidateArrange before you call ListBox.UpdateLayout.
Use INotifyPropertyChanged is the best way, refresh the entire list is not a good idea.
Main entrance:
public partial class MainWindow : Window
{
private BindingList<FoodModel> foodList = new BindingList<FoodModel>();
public MainWindow()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
foodList.Add(new FoodModel { foodName = "apple1" });
foodList.Add(new FoodModel { foodName = "apple2" });
foodList.Add(new FoodModel { foodName = "apple3" });
FoodListBox.ItemsSource = foodList;
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
foodList[0].foodName = "orange";
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
FoodListBox.Items.Refresh();
}
}
Model:
public class FoodModel: INotifyPropertyChanged
{
private string _foodName;
public string foodName
{
get { return _foodName; }
set
{
if (_foodName != value)
{
_foodName = value;
PropertyChanged(this, new PropertyChangedEventArgs("foodName"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
XAML:
<ListBox HorizontalAlignment="Center" Name="FoodListBox" VerticalAlignment="Top" Width="194" Height="150">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding foodName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Binding to model that contains collections in MVVM

I am getting started with MVVM (using Caliburn.Micro) and have come across an issue which I'm not sure if I'm doing this correctly. I have a model MediaCacherConfig which represents a textfile that stores the data in json format. The model contains 2 lists of strings and one string by itself.
What I am struggling with is how to correctly set up the viewmodel and in particular the AddNewFolder() method. I'm not sure if I am raising the correct event and whether the viewmodel's representation is correct. I can see how to bind to a simple property, but binding to a collection seems a bit more of a head spinner as I am creating a whole new collection everytime an item (string) is added.
Furthermore, when I load an entirely new model I have to run the NotifyPropertyChanged() method on all the properties which doesn't make sense to me.
Any guidance is much appreciated.
public class MediaCacherConfig : IConfig
{
public string DatabaseFileName { get; set; }
public ICollection<string> FoldersToScan { get; set; }
public ICollection<string> ExtensionsToIgnore { get; set; }
}
I have a viewmodel MediaCacherConfigViewModel:
public class MediaCacherConfigViewModel : PropertyChangedBase
{
private MediaCacherConfig Model { get; set; }
public string DatabaseFileName
{
get { return Model.DatabaseFileName; }
set
{
Model.DatabaseFileName = value;
NotifyOfPropertyChange(() => DatabaseFileName);
}
}
public BindableCollection<string> FoldersToScan
{
get
{
return new BindableCollection<string>(Model.FoldersToScan);
}
set
{
Model.FoldersToScan = value;
NotifyOfPropertyChange(() => FoldersToScan);
}
}
public BindableCollection<string> ExtensionsToIgnore
{
get
{
return new BindableCollection<string>(Model.ExtensionsToIgnore);
}
set
{
Model.ExtensionsToIgnore = value;
NotifyOfPropertyChange(() => ExtensionsToIgnore);
}
}
/* Constructor */
public MediaCacherConfigViewModel()
{
LoadSampleConfig();
}
/* Methods */
public void LoadSampleConfig()
{
MediaCacherConfig c = new MediaCacherConfig();
string sampleDatabaseFileName = "testing.config";
List<string> sampleFoldersToScan = new List<string>();
sampleFoldersToScan.Add("A");
sampleFoldersToScan.Add("B");
sampleFoldersToScan.Add("C");
List<string> sampleExtensionsToIgnore = new List<string>();
sampleExtensionsToIgnore.Add("txt");
sampleExtensionsToIgnore.Add("mov");
sampleExtensionsToIgnore.Add("db");
sampleExtensionsToIgnore.Add("dat");
c.DatabaseFileName = sampleDatabaseFileName;
c.FoldersToScan = sampleFoldersToScan;
c.ExtensionsToIgnore = sampleExtensionsToIgnore;
Model = c;
NotifyOfPropertyChange(() => DatabaseFileName);
NotifyOfPropertyChange(() => FoldersToScan);
NotifyOfPropertyChange(() => ExtensionsToIgnore);
}
public void AddNewFolder()
{
Model.FoldersToScan.Add("new one added");
NotifyOfPropertyChange(() => FoldersToScan);
}
public void SaveConfig()
{
ConfigTools.Configure(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Cacher", "Config"));
ConfigTools.SaveConfig(Model,"sampleconfig.txt");
}
public void LoadConfig()
{
ConfigTools.Configure(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Cacher", "Config"));
MediaCacherConfig m = ConfigTools.LoadConfig<MediaCacherConfig>("sampleconfig.txt") as MediaCacherConfig;
Model = m;
NotifyOfPropertyChange(() => DatabaseFileName);
NotifyOfPropertyChange(() => FoldersToScan);
NotifyOfPropertyChange(() => ExtensionsToIgnore);
}
}
And here is my view:
<UserControl x:Class="MediaCacher.Views.MediaCacherConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="413" Width="300">
<Grid MinWidth="300" MinHeight="300" Background="LightBlue" Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="409*"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<TextBox x:Name="DatabaseFileName" TextWrapping="Wrap" Margin="10,64,10,0" HorizontalAlignment="Center" Width="280" Height="42" VerticalAlignment="Top"/>
<ListBox x:Name="FoldersToScan" HorizontalAlignment="Left" Height="145" Margin="10,111,0,0" VerticalAlignment="Top" Width="280"/>
<ListBox x:Name="ExtensionsToIgnore" HorizontalAlignment="Left" Height="145" Margin="10,261,0,0" VerticalAlignment="Top" Width="280"/>
<Button x:Name="AddNewFolder" Content="Add" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="87" Height="49"/>
<Button x:Name="LoadConfig" Content="Load" HorizontalAlignment="Left" Margin="102,10,0,0" VerticalAlignment="Top" Width="96" Height="49"/>
<Button x:Name="SaveConfig" Content="Save" HorizontalAlignment="Left" Margin="203,10,0,0" VerticalAlignment="Top" Width="87" Height="49"/>
</Grid>
First, here you are returning a brand new collection every time, so obviously nothing gets persisted.
public BindableCollection<string> FoldersToScan
{
get
{
return new BindableCollection<string>(Model.FoldersToScan);
}
set
{
Model.FoldersToScan = value;
NotifyOfPropertyChange(() => FoldersToScan);
}
}
Secondly, your AddFolder method should belong in your ViewModel. When you Add a string to your already existing collection the fact that it is a BindingCollection should fire off an event to your View automatically that a new Item was added.
This is how I would do it. This is obviously an example for demonstration purposes, please add everything else you need. Youd ideall want to pass EventArgs and note I am not implementing INotifyPorpertyChanged because I don't have time to write it all out. Also I am using ObservableCollection but you can use your BindableCollection.
The point of this example is to show you how to manage your ViewModel - > Model communcation. Technically speaking your View -> ViewModel should talk through a CommandPattern.
public class YourViewModel
{
private readonly YourModel model;
private ObservableCollection<string> foldersToScan = new ObservableCollection<string>();
public ObservableCollection<string> FoldersToScan
{
get { return this.foldersToScan; }
}
public YourViewModel(YourModel model)
{
this.model = model;
this.model.OnItemAdded += item => this.foldersToScan.Add(item);
}
public void AddFolder(string addFolder) //gets called from view
{
this.model.AddFolder(addFolder); //could be ICommand using Command Pattern
}
}
public class YourModel
{
private readonly List<string> foldersToScan;
public IEnumerable<string> FoldersToScan
{
get { return this.foldersToScan; }
}
public event Action<string> OnItemAdded;
public YourModel()
{
this.foldersToScan = new List<string>();
}
public void AddFolder(string folder)
{
this.foldersToScan.Add(folder);
this.RaiseItemAdded(folder);
}
void RaiseItemAdded(string folder)
{
Action<string> handler = OnItemAdded;
if (handler != null) handler(folder);
}
}

Categories