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);
}
}
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 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;
}
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.
I have 2 ViewModels
Case1ViewModel : ViewModelBase, ITestInterface
Case2ViewModel : ViewModelBase, ITestInterface
They both inherit from ITestInterface which is empty
I then have an
ObservableCollection<ITestInterface>
I then have a DataGrid I would like to databind to the ViewModels
At runtime it will be either a Case1ViewModel or Case2ViewModel , and I then want the datagrid to autogenerate the columns which will be different depending on which ViewModel is in the list(Only one type of ViewModel at a time), atm it is just empty (As it just looks at the interface) but I would think it would be possible to make this with some sort of reflection?
Example
The collection I bind to
public ObservableCollection<ITestInterface> Transactions
{
get { return _transactions; }
set
{
_transactions = value;
OnPropertyChanged(() => Transactions);
}
}
Example of a class in the list
public class WholesaleDialogTradeItemViewModel : ViewModelBase, ITestInterface
{
private string _tradeDirection;
private string _book;
private string _counterpart;
private int _volume;
private int _price;
private string _product;
private string _delivery;
private string _period;
public string TradeDirection
{
get { return _tradeDirection; }
set
{
_tradeDirection = value;
OnPropertyChanged(() => TradeDirection);
}
}
public string Book
{
get { return _book; }
set
{
_book = value;
OnPropertyChanged(() => Book);
}
}
public string Counterpart
{
get { return _counterpart; }
set
{
_counterpart = value;
OnPropertyChanged(() => Counterpart);
}
}
public string Product
{
get { return _product; }
set { _product = value; OnPropertyChanged(() => Product); }
}
public string Delivery
{
get { return _delivery; }
set { _delivery = value; OnPropertyChanged(()=> Delivery);}
}
public string Period
{
get { return _period; }
set { _period = value; OnPropertyChanged(() => Period);}
}
public int Volume
{
get { return _volume; }
set { _volume = value; OnPropertyChanged(() => Volume);}
}
public int Price
{
get { return _price; }
set { _price = value; OnPropertyChanged(() => Price);}
}
}
public interface ITestInterface
{
string Counterpart { get; }
}
The XAML
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid
Grid.Row="0"
Width="600"
SelectionMode="Extended"
SelectionUnit="FullRow"
CanUserDeleteRows="False"
CanUserResizeRows="False"
IsReadOnly="True"
AutoGenerateColumns="True"
AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn"
ItemsSource="{Binding Transactions, Mode=OneWay}">
</DataGrid>
<Grid
Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button
Grid.Column="0"
Margin="5,10,5,0"
cal:Click.Command="{Binding SaveCommand}"
Height="25">
<AccessText>_OK</AccessText>
</Button>
<Button
Grid.Column="1"
Margin="5,10,5,0"
cal:Click.Command="{Binding CancelCommand}"
Height="25">
<AccessText>_Cancel</AccessText>
</Button>
</Grid>
</Grid>
The result in this example is that I get the Counterpart string, but every other property from WholesaleDialogTradeItemViewModel is not displayed, if I remove Counterpart from the Interface nothing is shown
Another example (Right click on it to show full size)
The answer ended up with replacing
public ObservableCollection<ITestInterface> Transactions
with
public ObservableCollection<object> Transactions
Apparently that forces the compiler to use reflection to find out what to show, if I used the interface it would just take that
I have a simple MVVM project I am learning on. I am trying to add to an ObservableCollection through an ICommand command, but I am unable to?
MainWindow.cs I haven't added anything*
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.DataContext>
<Local:ViewModel></Local:ViewModel>
</Grid.DataContext>
<ListView Grid.Row="0" x:Name="lstNames" Margin="5,5,5,5" Grid.Column="1" ItemsSource="{Binding View_}">
<ListView.View>
<GridView x:Name="Setting_Items">
<GridViewColumn Header="Setting_A" DisplayMemberBinding="{Binding View_String}"/>
</GridView>
</ListView.View>
</ListView>
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="145,195,0,0"
Name="textBox1"
VerticalAlignment="Top"
Width="120" />
<ComboBox Height="23"
HorizontalAlignment="Left"
Margin="269,195,0,0"
Name="My_ComboBox"
VerticalAlignment="Top"
Width="222"
ItemsSource="{Binding View_}"/>
<Button Content="Message Text"
Height="23"
HorizontalAlignment="Left"
Margin="52,166,0,0"
Name="button1"
VerticalAlignment="Top"
Width="75"
CommandParameter="{Binding Text, ElementName=textBox1}"
Command="{Binding Print_Line}"/>
<Button Content="Add To Drop"
Height="23"
HorizontalAlignment="Left"
Margin="52,195,0,0"
Name="button2"
VerticalAlignment="Top"
Width="75"
/>
</Grid>
public class View
{
public string View_String {get; set;}
}
public class SimpleDelegateCommand : ICommand
{
Action<object> _executeDelegate;
public SimpleDelegateCommand(Action<object> executeDelegate)
{
_executeDelegate = executeDelegate;
}
public void Execute(object parameter)
{
_executeDelegate(parameter);
}
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
}
public class ViewModel
{
private ObservableCollection<View> _View;
public string _View_String { get; set; }
public ObservableCollection<View> View_
{
get { return _View; }
set { _View = value; }
}
ICommand _Print_Line = new SimpleDelegateCommand((x) => MessageBox.Show(x.ToString()));
ICommand _Add_Line = new SimpleDelegateCommand((x) =>
View_ = new ObservableCollection<View>() /////////Error HERE
{
new View(){View_String = x.ToString()}
}
);
public ViewModel()
{
View_ = new ObservableCollection<View>()
{
new View(){View_String = "Setting 1"},
new View(){View_String = "Setting 2"}
};
}
public ICommand Print_Line { get { return _Print_Line; } }
public ICommand Add_Line { get { return _Add_Line; } }
}
How do I go about adding to my ObservableCollection using an ICommand command? or how do I go about it?
Also, how do you perform multiple tasks using an ICommand command, for example:
ICommand _Print_Line = new SimpleDelegateCommand((x) => MessageBox.Show(x.ToString()); MessageBox.Show("Second task"));
perform multiple tasks:
_Print_Line = new SimpleDelegateCommand((x) => {
MessageBox.Show(x.ToString());
MessageBox.Show("Second task");
});
Add private set to your command's field, for access it just from your class
private ICommand print_Line;
public ICommand Print_Line {
get { return print_Line; }
private set { print_Line = value; }
}
private ICommand add_Line;
public ICommand Add_Line {
get { return add_Line; }
private set { add_Line = value; }
}
maybe can help this way too:
private ICommand print_Line;
public ICommand Print_Line { get { return print_Line; } }
private ICommand add_Line;
public ICommand Add_Line{ get { return add_Line; } }
Move your Commands Initialization code inside the constructor of your ViewModel class. You are trying to access View_ which is not yet constructed yet and hence giving error. Object initializer outside constructor should not call the instance methods/properties as the class is not constructed till that time.
ICommand _Print_Line;
ICommand _Add_Line;
public ViewModel()
{
_Print_Line = new SimpleDelegateCommand((x) => MessageBox.Show(x.ToString()));
_Add_Line = new SimpleDelegateCommand((x) =>
View_ = new ObservableCollection<View>() /////////Error HERE
{
new View(){View_String = x.ToString()}
}
);
View_ = new ObservableCollection<View>()
{
new View(){View_String = "Setting 1"},
new View(){View_String = "Setting 2"}
};
}
First of all, your code is barely readable. The error is pretty straight forward. When you initialize a field, you cannot reference a non-static field.
ICommand _Add_Line is an instance field. Just as _View is. If you want to reference it, initialize the ICommand _Add_Line within the constructor of the class. Every time you call a non-static field or method, you need an instance of that class to get its value.
There are several answers on this issue on stackoverflow