I have a raddataform control which uses ObservableCollection as input source and auto generates fields.I have implemented Insert and Edit Logic in the person class itself which implements IEditableObject and INotifyPropertyChanged through BeginEdit and EndEdit methods. but public void Delete() method wont work there.also I learned that ObservableCollection has CollectionChanged event which has NotifyChangedCollectionAction.Remove .So how can I implement delete(remove) logic on ObservableCollection so that it can delete corresponding field using linq?
Here's code :
public class EmployeeDataContext
{
private ICollectionView employees = null;
public ICollectionView Employees
{
get
{
if (this.employees == null)
{
ObservableCollection<Person> newEmployees = new ObservableCollection<Person>();
DataClassesDataContext db = new DataClassesDataContext();
var query = from c in db.EPersons
select c;
foreach (var q in query)
{
newEmployees.Add(new Person((DateTime)q.EStartingDate, q.EFirstName,q.ELastName, (Person.OccupationPositions) q.EOccupation,q.EPhoneNumber, (int)q.ESalary));
}
//newEmployees.CollectionChanged += (sender, args) =>
// {
// if (args.Action == NotifyCollectionChangedAction.Remove)
// }
return this.employees;
}
}
}
Extract the DataConext object from your event handler's evenargs on your codebehind for BeginEdit and EndEdit.
Then call your viewmodel's Delete method referencing the DataContext retieved.
I'm not 100% sure that I fully understand your question, but if you're asking how you can add a custom Remove method to the ObservableCollection<T> class, then you can use Extension Methods. Perhaps something like this:
public static class ExtensionMethods
{
public static bool Remove<T>(this ObservableCollection<T> collection)
{
var someObject;
// custom logic here
return collection.Remove(someObject);
}
}
Related
I am in a situation where I have to use a existing model which I am not allowed to change the existing Properties.
public class Car : ModelBase
{
private string _model;
public string Model
{
get { return _model; }
set { this.Update(x => x.Model, () => _model= value, _model, value); }
}
private IEnumerable<Person> _allowedDrivers;
public IEnumerable<Person> AllowedDrivers
{
get { return _allowedDrivers; }
set { this.Update(x => x.AllowedDrivers, () => _allowedDrivers=value, _allowedDrivers, value); }
}
}
Now we recently started implementing a WPF UI and I need to use these existing models. Is there any way I can use the IEnumerable and let it work like a ObservableCollection without changing it really? What are my options.
The thing is I remove a AllowedDriver and then add a AllowedDriver and the UI is not updating at all. This is logical, I then made(for testing purposes) the IEnumerable an ObservableCollection and then the UI works. Do I have any other options in keep using the IEnumerable but gets updated?
Is there any way I can use the IEnumerable and let it work like a
ObservableCollection without changing it really?
You can create another class which inherits your Car class. Since your ObservableCar inherits Car class you have access to AllowedDrivers property.
So, you can declare your desired observable collection and initialize with an observable collection converted from AllowedDrivers. This initialization should be inside get.
public class ObservableCar: Car {
public ObservableCar(){
_observableAllowedDriver = new ObservableCollection<Person>(AllowedDrivers);
}
private ObservableCollection<Person> _observableAllowedDriver;
public ObservableCollection<Person> ObservableAllowedDriver
{
get { return _observableAllowedDriver; }
}
}
You could simply assign an ObservableCollection to the AllowedDrivers property and operate on that collection:
var drivers = new ObservableCollection<Person>();
car.AllowedDrivers = drivers;
Now adding a new Person to the drivers collection will actually update the UI, because a bound UI element does a runtime check whether a collection implements INotifyCollectionChanged:
drivers.Add(new Person(...));
The above of course assumes that your ModelBase.Update method isn't doing anything strange, and eventually assigns the value argument of the property setter to its backing field _allowedDrivers so that the property getter returns the collection instance that was passed to the setter.
EDIT: If possible at all, it would make sense to change the Car class to use ICollection<T> instead of IEnumerable<T>:
public class Car : ModelBase
{
...
private ICollection<Person> _allowedDrivers;
public ICollection<Person> AllowedDrivers
{
get { return _allowedDrivers; }
set { this.Update(x => x.AllowedDrivers, () => _allowedDrivers=value, _allowedDrivers, value); }
}
}
You could then still assign an ObservableCollection, but get rid of the drivers variable:
car.AllowedDrivers = new ObservableCollection<Person>();
car.AllowedDrivers.Add(new Person(...));
Thanks for everyones input. I solved it via making/casting the IEnumerable to an ObservableCollection.
Like so:
public class Car : ModelBase
{
public Car()
{
AllowedDrivers = new ObservableCollection<Person>();
}
private string _model;
public string Model
{
get { return _model; }
set { this.Update(x => x.Model, () => _model= value, _model, value); }
}
private IEnumerable<Person> _allowedDrivers;
public IEnumerable<Person> AllowedDrivers
{
get { return _allowedDrivers; }
set { this.Update(x => x.AllowedDrivers, () => _allowedDrivers=value, _allowedDrivers, value); }
}
}
And then using it in my viewmodel or so I use it like this:
var allowedDrivers = (ObservableCollection<Person>)Car.AllowedDrivers;
allowedDrivers.Add(person)
So I have seen some responses to similar questions as this, but I was wondering if a certain paradigm that I am thinking of is even possible in C#. First, I'll lay out the issue:
I have a MVVM application that I am developing in C#. The model has properties that change, and when a single property changes in the model, it often times affects multiple properties in the view-model. So the view-model listens for changes on the model. And the view listens for changes on the view-model.
In my view-model, I end up getting some code that looks like this:
private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
string prop_name = e.PropertyName;
if (prop_name.Equals("some_property_on_the_model"))
{
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
NotifyPropertyChanged("some_property_on_the_view_model");
}
else if (...)
{
... etc ...
}
}
This gets annoying because it just seems messy. And if I forget to edit this function after adding a new property to the view-model then it can easily lead to bugs.
So here is what I would like to do, but I don't know if this is possible. So I would like one of you to help me understand if it is possible or not.
It would be really cool if I could use C#'s "attributes" feature to take care of the property changed propagation.
So maybe something like this:
[ListenToModelProperty("some_property_on_the_model")]
[OnPropertyChanged("MyButtonVisibility")]
public Visibility MyButtonVisibility
{
get
{
if (model.some_property_on_the_model == true)
{
return Visibility.Visible;
}
else
{
return Visibility.Hidden;
}
}
}
private void OnModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
prop_name = e.PropertyName;
foreach (var property in view_model)
{
var attributes = property.GetCustomAttributes(typeof(ListenToModelPropertyAttribute));
var descriptions = attributes.Select(x => x.Description);
if (descriptions.Contains(prop_name))
{
notification_to_make = property.GetCustomAttributes(typeof(OnPropertyChangedAttribute));
string notification_string = notification_to_make[0].Description;
NotifyPropertyChanged(notification_string);
}
}
}
Please note that the above code is not meant to be real code. It will definitely not compile and will not work. But I would like to see if something like the above is possible in C#. Is it possible to do something like this using attributes? Or is there a library out there that makes something like this possible?
I have figured out how to do it! It is fairly simple. I will post the relevant code here, and those who are interested can find all the code at this github repository that I just made: https://github.com/davepruitt/model-subscribe
First, I created a custom attribute class. It is a simple class that takes an array of strings as a parameter to its constructor. This allows you to listen to multiple properties on the model for changes. It looks like this:
using System;
using System.Collections.Generic;
namespace TestPropagationOfPropertyChanges
{
[AttributeUsage(AttributeTargets.All)]
public class ListenForModelPropertyChangedAttribute : System.Attribute
{
public List<string> ModelPropertyNames = new List<string>();
public ListenForModelPropertyChangedAttribute (string [] propertyNames)
{
ModelPropertyNames.AddRange (propertyNames);
}
}
}
I then created my model. For simplicity's sake, it only contains two properties. They are strings that store a "first name" and a "last name":
using System;
using System.ComponentModel;
namespace TestPropagationOfPropertyChanges
{
public class Model : NotifyPropertyChangedObject
{
#region Constructors
public Model ()
{
}
#endregion
#region Private data members
private string _first = string.Empty;
private string _last = string.Empty;
#endregion
#region Public properties
public string FirstName
{
get
{
return _first;
}
set
{
_first = value;
NotifyPropertyChanged ("FirstName");
}
}
public string LastName
{
get
{
return _last;
}
set
{
_last = value;
NotifyPropertyChanged ("LastName");
}
}
#endregion
}
}
The view-model, in this case, has a "full name" property. So it wants to listen to any changes that happen to the first or last name on the model, and then react to changes on either of those. I realize this isn't the best "real world" scenario in which this kind of system would be used, but it does help illustrate the concept. The first part of my view-model is below:
using System;
namespace TestPropagationOfPropertyChanges
{
public class ViewModel : NotifyPropertyChangedObject
{
#region Private data members
//This is public for testing purposes
public Model _model = new Model();
#endregion
#region Constructors
public ViewModel ()
{
_model.PropertyChanged += ReactToModelPropertyChanged;
}
#endregion
#region Properties
[ListenForModelPropertyChangedAttribute(new string [] {"FirstName", "LastName"})]
public string FullName
{
get
{
return _model.FirstName + _model.LastName;
}
}
Finally, the view-model finishes with the method that reacts to changes on the model. Normally, in a large and complex application, this method could contain a large if-else statement with lots of calls to NotifyPropertyChanged. Instead, we now just iterate through the properties of the view-model and see which ones subscribe to the model's property that was changed. See below:
void ReactToModelPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
//Get the name of the property that was changed on the model
string model_property_changed = e.PropertyName;
//Get a System.Type object representing the current view-model object
System.Type t = typeof(ViewModel);
//Retrieve all property info for the view-model
var property_info = t.GetProperties();
//Iterate through each property
foreach (var property in property_info)
{
//Get the custom attributes defined for this property
var attributes = property.GetCustomAttributes (false);
foreach (var attribute in attributes)
{
//If the property is listening for changes on the model
var a = attribute as ListenForModelPropertyChangedAttribute;
if (a != null)
{
//If the property that was changed on the model matches the name
//that this view-model property is listening for...
if (a.ModelPropertyNames.Contains(model_property_changed))
{
//Notify the UI that the view-model property has been changed
NotifyPropertyChanged (property.Name);
}
}
}
}
}
Overall, it works excellently, and is exactly what I needed. This code can easily be expanded upon to be even more functional for those interested.
Problem: Data changes but ListView does not update
I have a ListView whose ItemsSource is set to
<ListView ItemsSource="{Binding ContactsGrouped}"
On click of a button I update the query to only return records that contain the letters "Je". I can see that the right thing is being returned, and that ContactsGrouped is being updated, but the UI does not change.
public ObservableCollection<Grouping<string, Contact>> ContactsGrouped { get; set; }
where Grouping looks like this:
public class Grouping<K, T> : ObservableCollection<T>
{
public K Key { get; private set; }
public Grouping ( K key, IEnumerable<T> items )
{
Key = key;
foreach ( var item in items )
this.Items.Add( item );
}
}
Given that I'm using ObservableCollections, I'd expect the list to redraw. Am I missing something obvious?
I presume the Grouping class is utilised from a ViewModel. In which case that ViewModel has to implement the INotifyPropertyChanged interface such as below:
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged ([CallerMemberName]string propertyName = null)
{
if (PropertyChanged != null) {
PropertyChanged (this, new PropertyChangedEventArgs (propertyName));
}
}
#endregion
As long as you call the OnPropertyChnaged method on setting the property then you will get the results of the binding.
Turns out that while implementing INotifyPropertyChanged still won't update the list when filtering it. However, factoring out the code that populates the list in the VM and then calling that code in the OnTextChanged method (followed by a call to resetting the ItemsSource) does the trick.
public void OnTextChanged ( object sender, TextChangedEventArgs e ) {
vm.PopulateContacts( vm.CurrentDataService );
ContactListView.ItemsSource = vm.ContactsGrouped;
}
The PopulateContacts method looks like this (abridged)...
// setup
// Get the data
var sorted =
from contact in contacts
orderby contact.FullName
group contact by contact.FirstInitial
into contactGroup
select new Grouping<string, Contact> ( contactGroup.Key, contactGroup );
contactsGrouped = new ObservableCollection<Grouping<string, Contact>> ( sorted );
That works, and is reasonably clean and testable
I have an ScrollViewer which holds an ItemsSource. The items are bound to the ViewModel and are sorted in ascending order. The users are allowed to add items to this list, however, the list needs to be scrolled to bottom because of the sorting order. From what I found, ScrollViewer doesn't have a "lock to bottom" feature, but has a ScrollToEnd method which does what I'm looking for.
The problem though, is that the items are added in the ViewModel, and the ViewModel obviously doesn't have access to the View to call the ScrollToEnd method on the ScrollViewer. To get around this, I declared an action delegate in the ViewModel like this:
public Action ScrollAction { get; set; }
And set it in the View upon creation of the ViewModel:
viewModel.ScrollAction = () => scrollViewer.ScrollToEnd();
The delegate is executed in the ViewModel once an item is added to the list. Even though this works, it feels a little hacky to me, since this kind of breaks the isolation of the ViewModel from the View. Is there a better way to achieve this?
I would also vote for an AttachedProperty to your scroll viewer.
I created following attached property to bind scroll to end with a boolean variable.
public static class ScrollViewerBehavior
{
public static readonly DependencyProperty ScrollToRightEndProperty =
DependencyProperty.RegisterAttached("ScrollToRightEnd", typeof (bool), typeof (ScrollViewerBehavior),
new PropertyMetadata(false, OnScrollToRightEndPropertyChanged));
public static bool GetScrollToRightEnd(DependencyObject obj)
{
return (bool) obj.GetValue(ScrollToRightEndProperty);
}
public static void SetScrollToRightEnd(DependencyObject obj, bool value)
{
obj.SetValue(ScrollToRightEndProperty, value);
}
private static void OnScrollToRightEndPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
var sv = sender as ScrollViewer;
if (sv == null) return;
if ((bool) e.NewValue)
{
sv.ScrollToRightEnd();
}
else
{
sv.ScrollToLeftEnd();
}
}
}
You can use this attached property in your XAML...
<ScrollViewer ... local:ScrollViewerBehavior.ScrollToRightEnd="{Binding IsScrolledToEnd}" ... />
Alternatively if you want to save the action delegate as in your question, you could do the following in the OnScrollToRightEndPropertyChanged method above.
.....
var viewModel = sv.DataContext as YourViewModel;
if (viewModel != null)
{
viewModel.ScrollAction = () => sv.ScrollToRightEnd();
}
.....
He everybody,
I'm trying to setup a project management class.
In order to see if somthing in the data changed i want to implement events on the lower level of the programming structure. I have some Classes extending the ProjectComponent Class. The base class has an event and event throwing methode, which the childcomponents can use.
Now I have a couple of custom list (nameley eList) in the project object.
Because all the child component have a common parent, ProjectComponent, i would like my custom list object (eList) to subscribe to the event when an object is added and unsubscribe when removed.
However when trying to prog this, i received the following error:
'ProjectComponent' does not contain a
definition for 'itemChanged' and no
extension method 'itemChanged'
accepting a first argument of type
'ProjectComponent'
Which is kind of wierd seeing as the class clearly has that public field.
Here is a the code:
public class ProjectComponent
{
public event ItemChanged itemChanged;
public void throwItemChangedEvent(ItemChangedEventArgs Arguments)
{
if (itemChanged != null)
itemChanged(new Object(), Arguments);
}
}
public class eList<ProjectComponent> : IList<ProjectComponent>
{
List<ProjectComponent> internalList = new List<ProjectComponent>();
public override void Add(ProjectComponent Item)
{
this.internalList.Add(Item);
Item.itemChanged += new ItemChanged(ItemChanged_Handler);
}
private void ItemChanged_Handler(object sender, ItemChangedEventArgs eventArgs)
{
//do stuff here
}
}
An example how it would be called is:
public eList<ChildClass> Children = new eList<ChildClass>();
The idea is that when an object in the list is edited the list object recieve an object like so:
Children.childstring = "anything";
At the moment the field inside the Children object is changed an event could be recieved.
My question is simply what am i doing wrong, why cant i suscribe to the ProjectComponent event inside the eList class?
Or does anyone know a better way to achive the same results?
Thanks in Advance,
Harry
Edit: Definition of ItemChanged delagate:
public delegate void ItemChanged(object sender, ItemChangedEventArgs eventArgs);
public class ItemChangedEventArgs : EventArgs
{
private String p_CallStack;
public String CallStack
{
get { return this.p_CallStack; }
set { this.p_CallStack = value; }
}
public ItemChangedEventArgs()
{
p_CallStack = "";
}
public ItemChangedEventArgs(String StackStart)
{
p_CallStack = StackStart;
}
}
you have 2 errors:
1.
in generic class definition you must use variables not existing classes:
public class eList<ProjectComponent>: ...
--> public class eList<T>: ...
in your case you want to do:
public class eList : IList<ProjectComponent>
2.
Item.itemChanged += new Item.itemChanged(ItemChanged_Handler);
new Item.itemChanged has no meaning, you have to use the underlying delegate type of your event:
Item.itemChanged += new ItemChanged(ItemChanged_Handler);
N.B:
your code does not respect at all design guidelines for c#
More informations here:Naming Guidelines
Shouldn't it be
Item.itemChanged += new ItemChanged(ItemChanged_Handler);