Accessing ItemsSource from GridView - c#

I have a button, When it's clicked it populates my Datagrid. The code is written within the .xaml.cs file, which I believe breaks the MVVM rule but it's just a temporary situation. I know it's not ideal for MVVM.
Calculate.xaml.cs
public void PopulateGrid(object sender, RoutedEventArgs e)
{
BindableCollection<Payments> PaymentCollection = new BindableCollection<Payments>
....
Datagrid.ItemsSource = PaymentCollection
....
}
My question is if there's a way to read the Datagrids ItemsSource From the ViewModel.
What I've Tried
LoansViewModel
public BindableCollection<Payments> paymentCollection {get; set;}
Calculate.xaml
<telerik:RadGridView ItemsSource="{Binding paymentCollection, Mode=TwoWay}" ... />
The collection paymentCollection Doesn't Update after calculate is clicked.

Just do this the correct MVVM way. Get rid of your PopulateGrid method in the .xaml.cs file and eliminate setting the Click property in your xaml. Instead bind the command property of the button to an ICommand property in your ViewModel the same way you are binding the ItemsSource of the RadGridView. You will need an implementation of ICommand to use and MVVM Lights RelayCommand is one option for that.
Here is the code for the ICommand:
private ICommand _populateGridCommand;
public ICommand PopulateGridCommand
{
get
{
if (_populateGridCommand == null)
{
_populateGridCommand = new RelayCommand(() => PopulateGrid());
}
return _populateGridCommand;
}
}
public void PopulateGrid()
{
PaymentCollection.Clear();
//load data and then add to the collection
}
UPDATE
To do this in code behind, you'll need to access the ViewModel and work on the collection from it. I don't like this but it should work.
public void PopulateGrid(object sender, RoutedEventArgs e)
{
var loansVM = DataGrid.DataContext as LoansViewModel;
loansVM.paymentsCollection.Clear();
var newData = //load data
foreach (var data in newData)
loansVM.paymentsCollection.Add(data);
}

Your xaml code looks like it should work provided the DataContext of your grid is set to your ViewModel instance where your paymentCollection property is declared.
Once your binding is set, it calls the get on the paymentCollection property. If your collection property object is not reassigned any further, and you add and remove elements from it, and it notifies on those changes via INotifyCollectionChanged, it will work. This is how ObservableCollection works and used most commonly for such scenarios.
However, if when you calculate, you re-assign your paymentCollection property with a new instance, your grid will not update, because you now have an entirely different collection. In that case you will need to notify the view that the paymentCollection property itself has changed. In which case you should implement it as a notification property:
private BindableCollection<Payments>_paymentCollection;
public BindableCollection<Payments> paymentCollection {
get { return _paymentCollection; }
set {
_paymentCollection = value;
OnPropertyChanged("paymentCollection");
}
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}

Related

WPF Combo box not calling setter method when item changed

I have a combo box that is bound to an object from a model that is instantiated inside of my view model. OnPropertyChange is handled inside of the Notifier class that inherits from INotifyPropertyChange. The view model polls executes a method from a data access layer and returns an observablelist to the view model. This is then passed into a constructor that builds the object i want to bind to the combo box. The object has two properties. 1) An observable list of possible selections and 2) a string that represents the current selected item.
Here is the problem. The combo box is successfully bound and populated by the list. It does not however appear to call the setter method when an item is changed. I need this functionality so I can continue with application logic once the item is elected. Debugging confirms that no setter is called, only the get. The setter is in fact called on the model however which make sense. Im certain I am missing something here and am open to suggestions on a better way to do this.
Model
public class WellListGroup : Notifier
{
private ObservableCollection<string> _headers;
public ObservableCollection<string> headers
{
get { return _headers; }
set { _headers = value; OnPropertyChanged("headers"); }
}
private string _selected;
public string selected
{
get { return this._selected;}
set { this._selected = value; OnPropertyChanged("selected");}
}
}
Notifier
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
View Model
public class MainViewModel : Notifier
{
//data access layer
public static getWells gw = new getWells();
//set combo box
public static ObservableCollection<string> headers = gw.getHeaders();
private WellListGroup _wlg = new WellListGroup {headers = headers, selected = headers[0]};
public WellListGroup wlg
{
get {
return _wlg;
}
set {
_wlg = value;
OnPropertyChanged("wlg");
OnChange()// do stuff!!!
}
}
View
<ComboBox x:Name="groupComboBox"
DockPanel.Dock="Top"
ItemsSource = "{Binding Path = wlg.headers}"
SelectedItem="{Binding Path = wlg.selected, Mode=TwoWay}">
</ComboBox>
EDIT - Reworked ViewModel to Subscribe to event on the object
public class MainViewModel : Notifier
{
//data access layer
public static getWells gw = new getWells();
//set combo box
public static List<string> headers = gw.getHeaders();
private WellListGroup _wlg = new WellListGroup {headers = headers, selected = headers[0]};
public WellListGroup wlg
{
get {
return _wlg;
}
set {
_wlg = value;
OnPropertyChanged("wlg");
OnChange(_wlg.selected);// do stuff!!!
}
}
public MainViewModel()
{
// Move this into the constructor to avoid any race conditions
_wlg = new WellListGroup {headers = headers, selected = headers[0]};
// Subscribe to the property change even for WLG
_wlg.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == "selected") {
}
OnChange(_wlg.selected);// do stuff!!!
};
}
The situation is that the reflected property setter is within the _wlg class and not the setter of the _wlg class itself on the VM. The bounded item is not going to the top level but the lower property as mentioned.
Either put in a commanding system to kick off the OnChange()// do stuff!!! code or subscribe to the _wlg class instance INotifyProptertyChanged event and call the method you mentioned.
Is there anyway to handle either of those from within the view model?
Yes, subscribe to the instance of the class WellListGroup property changed event and look for selected or others to report a change.
public MainViewModel()
{
// Move this into the constructor to avoid any race conditions
_wlg = new WellListGroup {headers = headers, selected = headers[0]};
// Subscribe to the property change even for WLG
_wlg.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == 'selected')
OnChange()// do stuff!!!
};
}
Of note, it is unclear if you really need to hold the strings in an ObservableCollection. That collection has its own implementation of notify events for adding and deleting of items within the collection.
If the VM needs that specific change info, then you will need to subscribe to the ObservableCollection's event(s) for such operations instead of/as well as the aforementioned above example.
If one does not need those notifications, holding the strings in an ObservableCollection is not needed and you can change it to a List<string> instead.

Transferring Textbox data to my ViewModel

I have two textboxes with userinput, of which I need to transfer the data to my ViewModel. I tried looking around how to do this by binding it to a button (as the transfer is supposed to take place upon a buttonclick), but most advice to use bindings. However, to use bindings you have to declare properties in the ViewModel (afaik), but as these strings are used to create a new object, holding properties for them would be all but ideal because the two textboxes might expand to over 10 in the future. I've also tried messing around with CommandParameter but I only seem to be able to declare one.
So for clarification:
How do I transfer the contents of two (or more) textboxes to the corresponding ViewModel so I can create a new Object with them?
Edit:
In addition I'd also like to be able to reset the Text= field to be empty once the method handling the data has succesfully completed.
The View
<TextBox Name="UI1"/>
<TextBox Name="UI2"/>
<Button Source="*ImageSource*" Command="{Binding CallCreateObject}"/>
and the ModelView
private void OnCallCreateObject()
{
Object newObject = new Object(UI1, UI2, false)
}
This is a general example of what I'm trying to achieve
If you want to insert data from UI to ViewModel on Button Click than there is no reason to use binding. Binding is mainly used to sync data between UI and underlying models.
Still if you want only that then on button_click event you can do something like this.
private void button_Click(object sender, RoutedEventArgs e)
{
Model model = new Model();
model.Property1 = textBox1.Text;
model.Property2 = textBox2.Text;
textBox1.Text = string.Empty;
textBox2.Text = string.Empty;
}
That will solve your issue. But this approach is not recommended when you have a better thing that is called 'Binding'
If you want to bind your view with a viewmodel then try this:
Your view model:
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Great, you have set up your view model. Now the view:
XML PersonView.xml:
<Grid Name="MyContainer">
<TextBox Text="{Binding PersonName}" />
<Button Name="SaveInfoButton" OnClick="SaveInfoButton_Click">Save info</Button>
</Grid>
Now that we have indicated with which property the textbox will be bind, lets indicate to the view the model that will use to update the property named PersonName. The idea is that when you click over the button, the property PersonName of our model Person gets updated with the value of the TextBox.
The xml class:
public partial class PersonView : UserControl
{
private readonly Person Model;
public PersonView()
{
//Components initialization, etc. etc...
this.Model = new Person();
this.DataContext = this.Model; // Here we are binding the model with our view.
}
private void SaveInfoButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(this.Model.PersonName); // this will print the value of your textbox.
}
}
Dont know if you noticed, but we didnt have the need of creating a new object when the user click the button. We just use our model and update the model properpies. If you add more textbox to your view, you'll have to added to our viewmodel as well as the given example.
Here is some post that can help you a little bit more(dont have enough time)
http://blog.scottlogic.com/2012/04/20/everything-you-wanted-to-know-about-databinding-in-wpf-silverlight-and-wp7-part-two.html
http://www.tutorialspoint.com/wpf/wpf_data_binding.htm
You could use bindings like this:
<TextBox Name="UI1" Text="{Binding Path=Ut1Value}"/>
<TextBox Name="UI2" Text="{Binding Path=Ut2Value}"/>
<Button Source="*ImageSource*" Command="{Binding CreateTheThingCommand}"/>
Then in your viewmodel you'll need to have the properties and command for those:
private string _ut1Value;
private string _ut2Value;
public string Ut1Value
{
get
{
return _ut1Value;
}
set
{
if (_ut1Value!= value)
{
_ut1Value= value;
OnPropertyChanged("Ut1Value");
}
}
}
public string Ut2Value
{
get
{
return _ut2Value;
}
set
{
if (_ut2Value!= value)
{
_ut2Value= value;
OnPropertyChanged("Ut2Value");
}
}
}
public ICommand CreateTheThingCommand
{
get { return new RelayCommand(CreateTheThing); }
}
private void CreateTheThing()
{
Object newObject = new Object(_ut1Value, _ut2Value, false);
// Do whatever with your new object
}
It sounds as if you need at least two ViewModel objects:
One to present the data from an existing object. This would be, essentially, what you have already.
A container ViewModel. This encapsulates the behaviours of the IEnumerable collection of objects, including the functionality required to Add a new object.
The container ViewModel would have the properties that you are struggling with, plus the CreateObject command, along with an IEnumerable (ObservableCollection) property to hold the existing ViewModel objects.
In your View, you would have one control to present the data in an existing ViewModel object, and a second control with a ListView (or similar) control to display the existing view controls and the set of TextBox controls, plus the button to create a new object (and add it to the list).
This would also allow you to add 'remove', 'sort', etc. functionality to the container ViewModel, without having to change the existing ViewModel.
A way to accomplish a scalable solution with minimal lines of code, would be to create hold a list of items you bind to in the view model.
This way you can use an ItemsControl in the UI to display a textbox for each item:
public class ViewModel
{
public List<Item> Items {get;} = new List<Item>
{
new Item { Value = "UI1" },
new Item { Value = "UI2" },
};
public class Item
{
public string Value {get;set;}
}
}
View:
<ItemsControl ItemsSource="{Binding Test}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}" Margin="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Commit" Margin="5" Click="ButtonBase_OnClick"/>
You can then create the object either from a click event or command:
private void OnCallCreateObject()
{
Object newObject = new Object(Items[0], Items[1], false);
}
The downside is that the order of the items is not explicit, so either you need to assume that the indexed order is correct, or order them manually.

WPF DataGrid binding to List<Type>

I'm trying to bind a WPF DataGrid to a List<ClassName>.
Below is my DataGrid:
<DataGrid ItemsSource="{Binding Source=FileProcessing}" AutoGenerateColumns="True"></DataGrid>
Below I am binding the list with database records:
FileProcessing = GetFileProcessingInfo(dtDateStart, dtDateEnd);
The FileProcessing is defined as a property below:
public List<FileProcessing_T> FileProcessing { get; set; }
The GetFileProcessingInfo Method also returns a List<FileProcessing_T> object.
The FileProcessing list does get some records from the database but the grid does not bind the data from the list.
I will appreciate your help.
You can keep your databinding.
But your have to implement the INotfifyPropertyChanged interface in the class where the FileProcessing property is located.
Because in the setter of FileProcessing you have to perform the change notification.
public ObservableCollection<FileProcessing_T> FileProcessing
{
get
{
return _fileProcessing;
}
set
{
if (_fileProcessing != value)
{
_fileProcessing = value;
RaisePropertyChanged("FileProcessing");
}
}
}
ObservableCollection<FileProcessing_T> _fileProcessing;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Otherwise the UI control will not know (not be notified) that the bound data has changed.
This will be enough to fix your problem.
It would even work if you continued to use List<FileProcessing_T> instead of ObservableCollection<FileProcessing_T>, however the ObservableCollection also supports change notifications if single elements are added and removed from the collection while List does not.

WPF ComboBox Bound List set on DropDownOpened Event

My problem is that the ComboBox is not displaying the value stored in its bound list.
Here is what I'm doing:
WPF:
<ComboBox ItemsSource="{Binding Devices}"
DropDownOpened="deviceSelector_DropDownOpened"/>
Note that my Window's DataContext is {Binding RelativeSource={RelativeSource Self}}.
C# code-behind:
public List<String> Devices { get; set; }
private void deviceSelector_DropDownOpened(object sender, EventArgs e)
{
// the actual population of the list is occuring in another method
// as a result of a database query. I've confirmed that this query is
// working properly and Devices is being populated.
var dev = new List<String>();
dev.Add("Device 1");
dev.Add("Device 2");
Devices = dev;
}
I have tried doing this with an ObservableCollection instead of a List, and I've also tried using a PropertyChangedEventHandler. Neither of these approaches have worked for me.
Any idea why my items aren't being displayed when I click the dropdown?
Since you're doing this in code behind anyway, why not set the ComboBox.ItemsSource directly.
Now, I am not going to say this is the way it should be done in WPF (I would prefer the view's data to be loaded in a ViewModel), but it will solve your issue.
The reason why this isn't working is because your property doesn't inform the binding system when it changes. I know you said you tried it with PropertyChangedEventHandler, but that won't work unless your View looks like this:
public class MyView : UserControl, INotifyPropertyChanged
{
private List<String> devices;
public event PropertyChangedEventHandler PropertyChanged;
public List<String> Devices
{
get { return devices; }
set
{
devices = value;
// add appropriate event raising pattern
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Devices"));
}
}
...
}
Likewise, using an ObservableCollection would only work like this:
private readonly ObservableCollection<string> devices = new ObservableCollection<string>();
public IEnumerable<string> Devices { get { return devices; } }
private void deviceSelector_DropDownOpened(object sender, EventArgs e)
{
devices.Clear();
devices.Add("Device 1");
devices.Add("Device 2");
}
Either method should populate the ComboBox, and in a quick test I just ran, it worked.
Edit to add DependencyProperty method
One last way you can do this is with a DependencyProperty (as your View is a DependencyObject:
public class MyView : UserControl
{
public static readonly DependencyProperty DevicesProperty = DependencyProperty.Register(
"Devices",
typeof(List<string>),
typeof(MainWindow),
new FrameworkPropertyMetadata(null));
public List<string> Devices
{
get { return (List<string>)GetValue(DevicesProperty); }
set { SetValue(DevicesProperty, value); }
}
...
}
The following change (suggested by Abe Heidebrecht) fixed the problem, but I don't know why. Anyone willing to lend an explanation?
WPF:
<ComboBox DropDownOpened="deviceSelector_DropDownOpened"
Name="deviceSelector"/>
C# code-behind:
private void deviceSelector_DropDownOpened(object sender, EventArgs e)
{
var dev = new List<String>();
dev.Add("Device 1");
dev.Add("Device 2");
deviceSelector.ItemsSource = dev;
}
Unless I'm missing something here:
Try firing OnPropertyChanged when devices gets updated for the devices property, this should fix this. I have occasionally also had to set the mode:
ItemsSource="{Binding Devices, Mode=TwoWay}"
On some controls.
Setting the itemssource on the control directly tells the control to use new items directly, without using the binding hooked up in the xaml. Updating the Devices property on the datacontext does not tell the combobox that the Devices property has changed so it won't update. The way to inform the combobox of the change is to fire OnPropertyChanged for the devices property when it gets changed.

DependencyProperty binding mode twoway but propertychangedeventhandler is null

I'm trying to follow the MVVM design paradigm with C# and XAML. I'm running into trouble with a nested user control. I'm trying to bind an element on the nested user control to one of the values in the ViewModel (which is bound to the View via the DataContext property). The same ViewModel is used for both the outer and nested user controls.
It partially works as is, but changes only go one-way from the ViewModel to the nested user control. I need the changes made in the nested user control to propagate back to the ViewModel.
Starting with the XAML for the main View, I have:
<UserControl>
<!-- ... -->
<UserControl.DataContext>
<local:MyViewModel x:Name="myViewModel" />
</UserControl.DataContext>
<!-- ... -->
<local:NestedUserControl
x:Name="nestedUserControl"
CustomNestedValue="{Binding Path=CustomValue, ElementName=myViewModel, Mode=TwoWay}" />
</UserControl>
In the C# code for the ViewModel:
// Constructor
public MyViewModel()
{
CustomValue = true;
}
private bool _customValue;
public bool CustomValue
{
get { return _customValue; }
set
{
if (_customValue != value)
{
_customValue = value;
RaisePropertyChanged ("CustomValue");
}
}
}
And in the code behind of the NestedUserControl, I have:
public static readonly DependencyProperty CustomNestedValueProperty =
DependencyProperty.Register (
"CustomNestedValue",
typeof (bool),
typeof (NestedUserControl),
new FrameworkPropertyMetatdata
{
BindsTwoWayByDefault = true,
PropertyChangedCallback =
new PropertyChangedCallback (CustomNestedValueChangedCallback)
});
public bool CustomNestedValue
{
get { return (bool) GetValue (CustomNestedValueProperty); }
set { SetValue (CustomNestedValueProperty, value); }
}
protected static void CustomNestedValueChangedCallback (
DependencyObject Source,
DependencyPropertyChangedEventArgs e)
{
bool value = (bool) e.NewValue;
NestedUserControl control = source as NestedUserControl;
control.OnCustomValueChange (value);
}
public void OnCustomValueChange (bool value)
{
RaisePropertyChanged ("CustomNestedValue");
// Do other stuff ...
}
// This function is where the nested user control gets direct
// interactions from the user which cause the dependency
// property to change. When this event occurs, the change needs
// to be communicated back up to the view model.
private void _onPreviewMouseDown (object sender, MouseButtonEventArgs e)
{
CustomNestedValue = !CustomNestedValue;
}
[Note: Not only do I set the binding mode to TwoWay when setting the binding in XAML, but I attempted to make this the default behavior of the DependencyProperty in the code above. No luck.]
Both the code behind for the nested user control and the ViewModel code contain the below PropertyChangedEventHandler event/response, which is necessary for the INotifyPropertyChanged interface. From what I understand, this is how bindings between XAML elements and the ViewModel are kept in sync.
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
try
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
catch (Exception e)
{
// ...
}
}
When I run the code, whenever the RaisePropertyChanged function is called for the NestedUserControl, the PropertyChanged event is always null. This is only a problem for the nested usercontrol, and not the outer one. Shouldn't this event be automatically set via the binding mechanism?
I've been struggling with this for several days now to no avail. Any help would be much appreciated. Thanks!
Binding to a DependencyObject operates without using the INotifyPropertyChanged interface. In fact, if you set a breakpoint in the getter or setter of the CustomNestedValue property of the NestedUserControl, you'll find it will never hit when binding in XAML. In essence, the INotifyPropertyChanged is a way of achieving binding without descending from DependencyObject.
When the MyViewModel.CustomValue is bound to the NestedUserControl, the binding code calls (in pseudo code):
NestedUserControl.SetBinding(binding, NestedUserControl.CustomNestedValueProperty)
The INotifyPropertyChanged.PropertyChanged event is never registered and will remain null. However, this doesn't necessarily answer why the value isn't going back to the ViewModel.
Regardless, you could remove a few moving pieces and go with
public static readonly DependencyProperty CustomNestedValueProperty =
DependencyProperty.Register("CustomNestedValue",
typeof (bool),
typeof (NestedUserControl),
null);
public bool CustomNestedValue
{
get { return (bool) GetValue (CustomNestedValueProperty); }
set { SetValue (CustomNestedValueProperty, value); }
}
That's how most of my DependencyProperties are written and they do support TwoWay binding.

Categories