I am trying to figure this out, given the following code, does the Refresh() need to occur on the UI thread? It seems to work, and I am wondering if the CollectionViewSource is actually a thread-aware / safe object? It definately has properties and methods to support calling on the correct thread, just not sure if that is left up to the developer, or if this is accomplished within the object?
public CollectionViewSource UserList { get; private set; }
void setupCollections()
{
UserList = new CollectionViewSource();
UserList.Source = searchProvider.UserResults;
UserList.SortDescriptions.Add(new SortDescription("DisplayName", ListSortDirection.Ascending));
}
Is this Thread safe in Silverlight???
void RefreshUserList()
{
UserList.View.Refresh();
}
Or do you need to do something like this?
void RefreshUserList()
{
// Is This Required?
UserList.Dispatcher.BeginInvoke(() =>
{
UserList.View.Refresh();
});
// Or MVVM-light Method
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
UserList.View.Refresh();
});
}
Per Microsoft's Documentation on CollectionViewSource the CollectionViewSource object is not Thread-safe. It seems that this is not reported to be Thread-safe, even though it does seems to work in many situations.
This may be because the method being called is actually on the View, not the CollectionViewSource. The View returns an ICollectionView interface - the details of the supporting class are not known, except that the CreateView() method creates this.
I would suggest that we always consider this not thread-safe and dispatch it to the correct thread, although my testing of the View.Refresh() at least suggests that it is thread-safe.
Related
Specifically this is a follow-up to this question DataGrid filter performance, but there are many more similar questions relating to WPF DataGrid performance on StackOverflow.
After a lot of profiling and going through .NET source code, I have realized that many performance issues, such as filtering and sorting, boil down just one issue: A CollectionView.Reset event does not recycle containers (like scrolling does).
What I mean is that instead of assigning the existing rows a new datacontext, all rows are removed from the visual tree, new rows are generated and added, and a layout cycle (measure and arrange) is performanced.
So the main question is: Has anyone successfully managed to work around this? E.g. by manually manipulating the ItemContainerGenerator, or by creating their own version of the DataGridRowsPresenter?
So this is the gist of my approach so far.
public class CollectionViewEx
{
public event EventHandler Refresh;
public override void Refresh()
{
Refresh?.Invoke(this, EventArgs.Empty);
}
}
public class DataGridEx : DataGrid
{
protected override OnItemsSourceChanged(IEnumerable oldSource, IEnumerable newSource)
{
if (newSource is CollectionViewEx cvx)
{
cvx.Refresh += (o,a) => OnViewRefreshing;
}
}
private void OnViewRefreshing()
{
RowsPresenter.Refresh();
}
}
public class DataGridRowsPresenterEx : DataGridRowsPresenter
{
public void Refresh()
{
var generator = (IRecyclingItemContainerGenerator)ItemContainerGenerator;
generator.Recycle(new GeneratorPosition(0, 0), ???);
RemoveInternalChildRange(0, VisualChildrenCount);
using (generator.StartAt(new GeneratorPosition(-1, 0), GeneratorDirection.Forward))
{
UIElement child;
bool isNewlyRealised = false;
while ((child = generator.GenerateNext(out isNewlyRealised) as UIElement) != null)
{
AddInternalChild(child);
generator.PrepareItemContainer(child);
}
}
}
}
But the results are very confusing - obviously because I don't quite understand how to work with the ICG.
I have looked through .net source code to see their implementations (when adding/removing/replacing items), and also found a couple of online resources on how to create a new virtualized panel (e.g. virtualizingwrappanel), but none really address this particular issue, where we want to reuse all existing containers for a new set of items.
So the secondary question is: Can anyone explain if this approach is even possible? How would I do it?
I never use reset directly from CollectionView because a methode on CollectionView's Source do it. This IList is modified for my needs. I did it like Paul McClean explained here.
In this class you could notify OnCollectionChanged to inform the CollectionView. sondergard explained what NotifyCollectionChangedAction.Reset do. But NotifyCollectionChangedAction.Replace keep running the recyling for the Items.
Maybe my research helps.
I'm using WPF, MVVM and Entity Framework in my current project.
To keep things simple, let's say I have a viewmodel for CRUD operations towards a list of materials (Solid woods).
My ViewModel's EF context (WTContext) is initialized through property injection, for instance:
SolidWoods_VM newView = new SolidWoods_VM();
newView.Context = new WTContext(SettingsManager.Instance.GetConnectionString());
This way I'm able to test this ViewModel:
SolidWoods_VM swVM = new SolidWoods_VM();
swVM.Context = new FakeWTContext();
Imagine that during a insert operation something goes wrong and the WTContext.SaveChanges() fails.
What is the best way to refresh the ViewModels context?
Create a new bool property in the viewmodel named ForTestingPurposes, and when the SaveChanges method fails:
try
{
Context.SaveChanges();
}
catch
{
if (!ForTestingPurposes)
{
Context = new WTContext(SettingsManager.Instance.GetConnectionString());
}
}
Send a message to the mainviewmodel for context reloading (through mediator pattern):
Mediator.Instance.NotifyColleagues<SolidWoods_VM>(MediatorMessages.NeedToUpdateMyContext, this);
(Yet, this way I'd still need the bool property)
3.A more elegant solution, without aditional properties, provided for you guys :)
Why not abstract the methods/properties you need on your data context onto an interface and then have an implementation of that that handles the exception.
//WARNING: written in SO window
public interface IDataSource
{
void SaveChanges();
//... and anything else you need ...
}
public class RealDataSource : IDataSource
{
private WTContext _context;
public void SaveChanges()
{
try { _context.SaveChanges(); }
catch
{
_context = new WTContext(/*...*/);
}
}
}
This way you can still implement a fake/mock data source but your view model class doesn't need to know anything about how the data is actually retrieved.
My opinion is that your best bet would be the message.
You need a way to indicate that the save went wrong, and it might not serve all consumers of the class to have the context regenerated. If you're binding to your VM in there, for example, resetting the context might have other UI consequences.
I am using the following code.
public partial class SettingApp
{
public SettingApp()
{
InitializeComponent();
Parallel.Invoke(SetDataInTextBox);
}
private void SetDataInTextBox()
{
txtIncAns.Text = Properties.Settings.Default.IncludeAN;
txtIncAuthor.Text = Properties.Settings.Default.IncludeAutt;
txtIncQuo.Text = Properties.Settings.Default.IncludeQU;
txtIncSpBegin.Text = Properties.Settings.Default.IncludeSP;
}
}
The program gives the following error
The calling thread cannot access this object because a different
thread owns it.
Which is the right way
update :
Whether this is right :
public partial class SettingApp
{
private delegate void SetDataInTextBoxDelegate();
public SettingApp()
{
InitializeComponent();
txtIncAns.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new SetDataInTextBoxDelegate(SetDataInTextBox));
}
private void SetDataInTextBox()
{
txtIncAns.Text = Properties.Settings.Default.IncludeAN;
txtIncAuthor.Text = Properties.Settings.Default.IncludeAutt;
txtIncQuo.Text = Properties.Settings.Default.IncludeQU;
txtIncSpBegin.Text = Properties.Settings.Default.IncludeSP;
}
}
Only the UI thread can access UI elements, which I'm guessing is what those txt things are. Parallel.Invoke is in your case not the UI thread, so the exception is thrown when you try to access the .Text property on the controls.
You need to marshal the call across to the UI thread. In WinForms, controls have various ways to help you do this:
if (myControl.InvokeRequired)
{
myControl.Invoke(...);
}
else
{
myControl.Text = "something";
}
MSDN has an article with examples on it here (VS2010):
http://msdn.microsoft.com/en-us/library/757y83z4(v=VS.100).aspx
Update 1:
For WPF the model is similar, but includes the Dispatcher:
myControl.Dispatcher.Invoke(...);
MSDN Forum Post
MSDN Article
Update 2: Of course, it looks like you don't even need to use multi-threaded code here. I would guess the overhead of using the multi-threaded portion is more than the code you eventually call. Simply remove the use of multiple threads from this section and set the properties directly:
public SettingApp()
{
InitializeComponent();
SetDataInTextBox();
}
private void SetDataInTextBox()
{
txtIncAns.Text = Properties.Settings.Default.IncludeAN;
txtIncAuthor.Text = Properties.Settings.Default.IncludeAutt;
txtIncQuo.Text = Properties.Settings.Default.IncludeQU;
txtIncSpBegin.Text = Properties.Settings.Default.IncludeSP;
}
As Adam said, only the UI thread can access UI elements. In the case of WPF, you'd use myControl.Dispatcher.Invoke().
Since these calls are all going to be invoked on the UI thread anyway, you should remove the Parallel.Invoke() and call the method directly.
Just an alternate suggestion. I would move toward databinding your textboxes to properties of a class, even if you don't want to go the full MVVM design, databinding is your friend in WPF. Then if you need the threading, WPF will handle updating controls on UI thread when the property changes, even when the property is changed on another thread.
Typically in the property setter of an object we may want to raise a PropertyChanged event such as,
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public string UserNote
{
get { return _userNote; }
set
{
_userNote = value;
Notify("UserNote");
}
}
In our existing code base I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed. It also seems to causes issues where objects update each other in a circular fashion.
Is this ever a good practice?
A comment in the code tries to justify it ...
//The purpose of this method is to wire up clients of NotificationBase that are also
//NotificationBases to *their* clients. Consider the following classes:
public class ClassA : NotificationBase
{
public int Foo
{
get { return 123; }
set { Notify("Foo"); }
}
}
public class ClassB : NotificationBase
{
ClassA A = new ClassA();
public ClassB()
{
A.PropertyChanged += AllChanged;
}
public void SetFoo()
{
A.Foo = 456;
}
}
public class ClassC
{
ClassB B = new ClassB();
public ClassC()
{
B.PropertyChanged += delegate { dosomething(); };
B.SetFoo(); // causes "dosomething" above to be called
}
}
/// ClassB.SetFoo calls ClassA.Foo's setter, which calls ClassA.Notify("Foo").
/// The event registration in ClassB's ctor causes ClassB.AllChanged to be called, which calls
/// ClassB.Notify(null) - implying that ALL of ClassB's properties have changed.
/// The event registration in ClassC's ctor causes the "dosomething" delegate to be called.
/// So a Notify in ClassA is routed to ClassC via ClassB's PropertyChanged event.
protected void AllChanged(Object sender, PropertyChangedEventArgs e)
{
Notify(null);
}
Any thoughts much appreciated.
Regards,
Fzzy
This is actually a problem with the design (or its documentation) of PropertyChangedEventArgs. Setting PropertyName to null means "all properties on this object have changed." But unless the class is sealed, or you're using reflection, you can't actually know that all properties on the object have changed. The most you can say is that all of the properties in the object's base class have changed.
This is reason enough to not use this particular convention, in my book, except in the vanishingly small number of cases where I create sealed classes that implement property-change notification.
As a practical matter, what you're really trying to do is just raise one event that tells listeners "a whole bunch of properties on this object have changed, but I'm not going to bother to tell you about them one by one." When you say:
I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed.
...the actual intent is the exact opposite. If a method changes the Foo, Bar, Baz, and Bat properties on an object, and the object has only four or five properties, raising one event is probably better than raising four. On the other hand, if the object has sixty properties, raising four events is probably better making every one of the object's listeners - even those that aren't looking at those four properties - do whatever they do when the properties that they care about change, because those properties didn't.
The problem is that the property-change notification system, as designed, isn't a fine-grained enough tool for every single job. It's designed to be completely generic, and has no knowledge of a particular application domain built into it.
And that, it seems to me, is what's missing from your design: application domain knowledge.
In your second example, if a Fixture object has (say) ten properties that depend on the value of FixtureStatus, raising ten property-change events may seem a little excessive. Maybe it is. Maybe the object should raise a FixtureStatusChanged event instead. Then classes with knowledge of your application domain can listen to this one event and ignore the PropertyChanged event. (You still raise the PropertyChanged event on the other properties, so that objects that don't know what a FixtureStatusChanged event means can stay current - that is, if it's still necessary for your class to implement INotifyPropertyChanged once you've implemented FixtureStatusChanged.)
A secondary comment: Most classes in the C# universe, if they implement a method that raises the Foo event, call that method OnFoo. This is an important convention: it makes the relationship between the method and the event explicit, and it makes the fact that the code that's calling the method is raising an event easy to recognize. Notify is a weak name for a method in general - notify who? of what? - and in this case it actually obfuscates something that should be made explicit. Property-change notification is tricky enough without your naming convention concealing the fact that it's happening.
Ignoring the other stuff, I'd say the Notify(null) alone is a bad practice. It's not inherently clear what that means, and to a developer working the code 5 years down the line would probably assume that it meant something else unless they happened upon the comments.
I have come across situations wherein computed properties (without setters) need to fire PropertyChangeNotification when some other property i set via a setter.
eg
double Number
{
get { return num;}
set
{
num=value;
OnPropertyChanged("Number");
OnPropertyChanged("TwiceNumber");
}
}
double TwiceNumber
{
get {return _num * 2.0;}
}
As a rule I only do it with get only properties and I don't see why in this case a property firing a change notification on the other is bad. But I think if I do it for any other case I most likely don't know what I am doing!
Imagine the following:
class Repository
{
private ObservableCollection<ModelClass> _allEntries;
public ObservableCollection<ModelClass> AllEntries
{
get { return _allEntries; }
set { _allEntries = value; }
}
public void RefreshDataFromDB()
{
_all = new ObservableCollection(GetMyData()); // whatever method there is
}
}
Now there are a couple of controls that bind to this collection, e.g.:
<ListView ItemsSource="{Binding Repository.AllEntries, ElementName=Whatever}"/>
The problem now is that if I call the RefreshDataFromDB the bindings get lost (at least it seems so) as the _all is now pointing to new memory part and the bindings still use the old reference. INotifyPropertyChanged does not help me in this case (e.g. putting it in RefreshDataFromDB does not help a lot).
The question would be - how would you handle a case where you replce a collection and want to update its consumers' bindings?
Yes; you're not modifying the collection, the UI is bound to the collection, and then you replace it with a new one.
You could do this:
_all.Clear();
_all.AddRange(GetMyData());
Hope that helps!
Alternatively, make AllEntries (or All.. your nomenclature seems to change a few times on the post ;)) a DependencyProperty:
public static DependencyProperty AllEntriesProperty = DependencyProperty.Register("AllEntries", typeof(ObservableCollection), typeof(MyClass));
You'd need to make the get/set property too, see here for an example:
http://msdn.microsoft.com/en-us/library/ms752914.aspx