INotifyPropertyChanged and INotifyCollectionChanged in F# using WPF - wpf

I have an array of sample information that is constantly refreshed in a background thread.
Currently I am constantly assigning this array to a datagrid's ItemsSource property using a DispatcherTimer. That works but it resets any visual locations, for instance if the user places his cursor in the middle of the datagrid the execution timer will undo such position.
Is it possible to use a INotifyPropertyChanged or INotifyCollectionChanged event for this instead to prevent such situations to occur? If so, how does this work with F#?
I suppose I have to execute some function notifying the datagrid every time when there is an update of the array. The updating of the array is not in the STAThread section.
I am running VS2010 with the latest WPF toolkit containing the WPF datagrid.

You can use an ObservableCollection which will implements INotifyCollectionChanged for you. The F# looks something like this:
open System
open System.Collections.ObjectModel
open System.Windows
open System.Windows.Controls
open System.Windows.Threading
[<EntryPoint; STAThread>]
let Main args =
let data = ObservableCollection [0 .. 9]
let list = ListBox(ItemsSource = data)
let win = Window(Content = list, Visibility = Visibility.Visible)
let rnd = Random()
let callback =
EventHandler(fun sender args ->
let idx = rnd.Next(0, 10)
data.[idx] <- rnd.Next(0, 10)
)
let ts = TimeSpan(1000000L)
let dp = DispatcherPriority.Send
let cd = Dispatcher.CurrentDispatcher
let timer = DispatcherTimer(ts, dp, callback, cd) in timer.Start()
let app = Application() in app.Run(win)
Unfortunately Reflector shows that System.Windows.Controls.ItemsControl.OnItemCollectionChanged method removes the selection when it is called, so you may need to work around this default behaviour.
You can also implement INotifyPropertyChanged like so:
open System.ComponentModel
type MyObservable() =
let mutable propval = 0.0
let evt = Event<_,_>()
interface INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = evt.Publish
member this.MyProperty
with get() = propval
and set(v) = propval <- v
evt.Trigger(this, PropertyChangedEventArgs("MyProperty"))
Implementing INotifyCollectionChanged would work similarly.
best of luck,
Danny

The ObservableCollection works but unfortunately with issues.
Since the ObservableColection only works when modified in the STAThread, I have to use a dispatcher and basically rewrite, or at least inspect, the complete array as I cannot tell which entries are changed or not.
One thing that is a possibility is to use a F# Mailbox. The background thread could place change messages which could be picked up by a dispatcher in the STAThread. This solution also would remove the need for synchronization.
Does that look like overkill? Anybody done that before? Any alternative solutions?

Related

"BindingSource cannot be its own data source" - error when trying to reset the binding source from a method in another class

We are binding a DataGridview using BindingSource. So in the main thread we have given like this.
class1BindingSource = new BindingSource();
class1BindingSource.DataSource = class1List;
this.dataGridView1.DataSource = class1BindingSource;
After that i have a placed a background worker in the form and is triggering in a button click.
i.e. in the button click
this.backgroundWorker1.RunWorkerAsync()
In the BackgroundWorker DoWork Event i am trying to update the BindingSource and there by trying to update the DataGridview.
So the BindingSource reset is done in a method in another class.
DoWork Event
Class2 cl2 = new Class2();
cl2.UpdateBindingSource(class1BindingSource);
UpdateBindingSource Method
public void UpdateBindingSource(BindingSource bs)
{
Class1 c1 = bs.Current as Class1;
for (int i = 0; i < 1000; i++)
{
lock (bs.SyncRoot)
{
c1.MyProperty1 = i;
bs.ResetItem(0);
}
}
}
Now i am getting an exception like BindingSource cannot be its own data source. Do not set the DataSource and DataMember properties to values that refer back to BindingSource.
If i am doing this in my DoWork Event then i can reset the item in the control thread itself using BeginInvoke method.
But actually i am trying to simulate our application scenario. So i want to solve this in this format.
Can any one help me on this.
The problem is that you can't update a BindingSource within a thread other than the gui thread. This is due the fact, that the BindingSource will fire some events which will then be received by your data grid view which will then start to update itself, which will fail cause it won't be done on the gui thread.
So right before you call RunWorkerAsync() you should call class1BindingSource.SuspendBinding() and within your RunWorkerCompleted you should call class1BindingSource.ResumeBinding().
Also ensure that within your DoWork you won't call any methods on the binding source (like you did with bs.ResetItem(0)).
And also remove this lock statement. It simply doesn't make any sense (in your example) and if you really need it (in your real code) consider using some private object _Gate = new Object(); within your class to avoid any deadlocks from the outer world, cause bs.SyncRoot is publicly available.
I had the same problem:
- BindingSource that had elements with INotifyPropertyChanged
- A separate Task that updated the elements.
The suggested solutions SuspendBinding etc didn't work. BindingSource should have done something like IsInvokeRequired.
Luckily Ivan Stoev came with the brilliant idea of subclassing the BindingSource and do something similar as IsInvokeRequired. Thank you Ivan!
Link: Update BindingSource from a different Task
UpdateBindingSource() does not take much time, so no need to use backgroundworker. You can invoke UpdateBindingSource() in the main thread.
Also, keep datagridview manipulation in the main thread.

Force binding in WPF

I'm writing tests which will check correctness of Binding elements specified in XAML. They work so far, the only issue is that I do not know how to correctly force databinding to happen. Surprisingly it is not enough to simply set something in DataContext, binding won't happen until you show your control/window. Please not that I'm writing 'unit'-tests and I'd like to avoid showing any windows.
Take a look at following code:
// This is main class in console application where I have all WPF references added
public class Program
{
[STAThread]
public static void Main()
{
var view = new Window();
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
view.DataContext = new int[5];
//view.Show(); view.Close(); // <-- this is the code I'm trying not to write
Console.WriteLine(view.Title);
}
}
Here I'm creating a Window and putting an array as DataContext to that window. I'm binding Window.Title to Array.Length so I expect to see number 5 printed in console. But until I Show window (commented line) I will get empty string. If I uncomment that line then I will receive desired 5 in console output.
Is there any way I can make binding happen without showing a window? It is pretty annoying to look at ~20 windows while launching tests.
P.S.: I know I can make windows more transparent and etc, but I'm looking for more elegant solution.
UPDATE Code above is simplified version of what I really have. In real code I receive a View (some UIElement with bindings) and object ViewModel. I do not know which exactly binding there were set on View, but I still want all of them to be initialized.
UPDATE 2: Answering to the questions regarding what I test and I why. I do not intend to test that classes like Binding, BindingBase, etc are working as expected, I assume they are working. I'm trying to test that in all my XAML files I have written bindings correctly. Because bindings are stringly typed things, they are not verified during compilation and by default they cause only errors in output window, which I'm missing occasionally. So if we take my example from above and if we will made a typo there in binding: {Binding Lengthhh} then my tests will notify you that there is no property with name Lengthhh available for binding. So I have around 100 XAML files and for each XAML I have a test (3-5 lines of code) and after launching my tests I know for sure that there are no binding errors in my solution.
The bindings are updated by the dispatcher with the DispatcherPriority.DataBind - so if you wait for a dummy task with SystemIdle priority you are sure that any pending databinding is done.
try
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
}
catch
{
// Cannot perfom this while Dispatcher in suspended mode
}
If you are trying to test correctness of your view, I suggest you test your view :-)
Why not run the UI from a unit test and write code that checks content of UI after changing data.
VS2010 does have GUI testing, or you could take a look at the code of tools such as Snoop.
Edit following comment:
If ALL you want to do is test a few simple bindings, try writing a static code test that runs as a post build event using reflection on view models and regular expressions on XAMLs. Add attributes on VM or use a config file so your test will know which view receives which View Model as DataContext. Compare property names and types in View Models with binding strings in View (automatically search XAML for these) and throw exception (thus failing build) if strings do not match.
If your bindings are more complex (converters, multibindings, ...) this may be a bit more complicated to implement.
I think you should first set the DataContext and then do the Binding, e.g.:
view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
I'm not sure if this is real solution for your general problem, but it works in this case.
I don't believe the Window's bindings will run without calling Show or ShowDialog, because that is the only way it gets associated with the UI message loop/dispatcher.
Your best bet would be to set it to be as least visible as possible, potentially using an extension method to clean things up:
public static void PokeWindowDispatcher(this Window window)
{
window.WindowState = WindowState.Minimized;
window.ShowInTaskbar = false;
window.Visibility = Visibility.None;
using (var wait = new ManualResetEvent())
{
Action<object, RoutedEventArgs> loaded = (sender, e) => wait.Set();
window.Loaded += loaded;
try
{
window.Show();
wait.WaitOne();
}
finally
{
window.Loaded -= loaded;
window.Close();
}
}
}
I had the same problem, and from sixlettervariables gave me an idea. It's very simple.
I am using WPF in WinForms application, so I use ElementHost control to host Wpf controls on WinForms control. To enforce WinForms control initialization you can just read value of Handle (which is actually Windows HWND) and this will force control to fully initialize itself including child ElementHost and all Wpf binding work.
I didn`t try to perform the same thing for pure Wpf control. But you can easily use ElementHost to initialize your Wpf controls like this:
var el = new ElementHost();
var p = new TextBlock();
p.DataContext = new { Data = "1234" };
p.SetBinding(TextBlock.TextProperty, "Data");
el.Child = p;
var t = el.Handle;
Debug.Assert(p.Text == "1234");
PS: Found, that everything work better, if you first set DataContext and only then force a Handle to be created (just like my example). But, I think, this is already the case for you, so should not be a problem.
Have you tryed to use the IsDataBound
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.isdatabound.aspx
Also check this out:
System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view).EnsureHandle();
http://msdn.microsoft.com/en-us/library/system.windows.interop.windowinterophelper.ensurehandle.aspx
My other question is why you trying to do a UNIT test on something that has been technically tested already? By the way I am not critising, just want to understand a little better.
Not sure, but maybe something like this will work?
view.GetBindingExpression(Window.TitleProperty).UpdateTarget();

WPF DataGrid bound to an ObservableCollection that is updated on separate thread fails to maintain sort

Download Sample Project
I have a wpf 4 datagrid that is bound to an ObservableCollection. This ObservableCollection is
updated on another thread. I can see the updates coming through to my simple gui just fine. I can even sort the data. But the sort does not "stick". It will sort once when you click the column header but when a value in the collection changes the sort does not change accordingly.
The MainWindow backing code is where most of the action goes down (just for simplicity of the example). I create an ObservableCollection and pass it to a thread that does the actual writes to the ObservableCollection. I then bind that same ObservableCollection to the datagrid via a CollectionView (I've tried binding it directly as well). My hunch is that the sorting depends upon the collectionChanged event which I'm pretty sure won't fire back to the Dispatcher ( see: http://bea.stollnitz.com/blog/?p=34).
What to do?
public partial class MainWindow : Window
{
private Thread _dataThread;
private Thread _marketThread;
private SampleData _sampleData;
private Market _market;
private ObservableCollection<Stock> stocks;
private ConcurrentQueue<Stock> _updates = new ConcurrentQueue<Stock>();
public MainWindow()
{
InitializeComponent();
stocks = new ObservableCollection<Stock>();
for (var i = 0; i < 5; i++)
{
var newStock = new Stock();
newStock.Id = (uint)i;
stocks.Add(newStock);
}
var source = CollectionViewSource.GetDefaultView(stocks);
dataGrid.ItemsSource = source;
_sampleData = new SampleData(_updates);
_dataThread = new Thread(_sampleData.CreateData) { Name = "Data Thread" };
_dataThread.Start();
_market = new Market(_updates, stocks);
_marketThread = new Thread(_market.Start){Name = "Market Thread"};
_marketThread.Start();
}
}
Challenge.
Download Sample Project
Have you looked at ObjectDataProvider IsAsynchonous="True" and bind in XAML? You might be able to not thread the collection creation. I have no experience how DataGrid sorts behave behind IsAsynchonous="True".
<ObjectDataProvider IsAsynchonous="True" ...>

Add f# function as event handler in xaml

Does anyone know how to add an F# event handler to a control in the xaml file?
I just gave a talk about reactive programming in F# (in London) that used Silverlight to implement most of the examples. The talk has been recorded and the samples are available for download as well, so this may be a useful resource:
See my blog with links to source, recording and slide
To answer your specific question, I don't think that you can use the usual style of specifying event handler in the XAML file (this may work in F# Silverlight Application, but you would have to use member instead of let function).
However, the best way (I can think of) for writing Silverlight components is to have just an F# Silverlight Library and use that from a C# Silverlight application. In that case, you need to write the event handler binding in code. A simplified example (from one of the samples from the talk) would look like this:
open System.Windows.Controls
// Dynamic invoke operator that makes accessing XAML elements easy
// (easier than using 'FindName' explicitly in the code
let (?) (this : Control) (prop : string) : 'T = // '
this.FindName(prop) :?> 'T // '
type MyControl() as this =
inherit UserControl()
do
let path = "/MyProject;component/MyControl.xaml"
let uri = new System.Uri(path, UriKind.Relative)
Application.LoadComponent(this, uri)
// Get Button named 'TestButton' from the XAML file
let btn : Button = this?TestButton
// Add event handler to the button
btn.Add(fun _ -> btn.Text <- "Clicked!")
Essentially you need to load a xaml file and find a control by name:
let w = Application.LoadComponent(new System.Uri(
"/twitterstream;component/Window.xaml", System.UriKind.Relative
)) :?> Window
let b = w.FindName("buttonToggle") :?> Button
and then you can simple add a handler an event:
b.Click.Add(fun _ -> ...)
You can get fancy and use first-class event combinators - here is a great step-by-step introduction:
Link

Inserting a new object into L2S table and databinding to it prior to SubmitChanges() in WPF

I'm just getting started with Linq-to-SQL and data binding in WPF, most of which works like a dream so far!
I've got (what I though was) a common scenario:
a) Query list of records from a table via datacontext and bind to the current user control
this.DataContext = db.ClientTypes;
b) Have the user see a bound ListView and some bound detail controls to make changes to the existing records, with a db.SubmitChanges(ConflictMode.FailOnFirstConflict); to push the changes back to the DB. No problem.
c) User wants to add a new record, so we:
ClientType ct = new ClientType();
ct.Description = "<new client type>";
db.ClientTypes.InsertOnSubmit(ct);
However at this point I dont want to call db.SubmitChanges as I want the user to be able to update the properties of the object (and even back out of the operation entirely), but I want them to be able to see the new record in the bound ListView control. Thinking I just needed to re-run the query:
ClientType ct = new ClientType();
ct.Description = "<new client type>";
db.ClientTypes.InsertOnSubmit(ct);
// Rebind the WPF list?
this.DataContext = db.ClientTypes;
listView1.SelectedItem = ct;
listView1.ScrollIntoView(ct);
However this doesn't work, the newly created record is not part of the returned list. I'm not sure if this is because of caching within L2S or if I'm just going about this the wrong way. Is there a better way to accomplish this?
Thanks.
Instead of setting your Control.DataContext = db.ClientTypes, store db.ClientTypes somewhere else and bind to an ObservableCollection that wraps it.
var somewhereElse = db.ClientTypes;
var toBind = new ObservableCollection<ClientType>(somewhereElse);
toBind.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
types.InsertAllOnSubmit<AddressType>(e.NewItems.Cast<AddressType>());
};
this.DataContext = toBind;
Then, when the user wants to add a new item:
ObservableCollection<ClientType> toBind = this.DataContext as ObservableCollection<ClientType>;
System.Diagnostics.Debug.Assert(toBind != null);
ClientType ct = new ClientType();
ct.Description = "<new client type>";
toBind.Add((ct);
Calling toBind.Add will cause the CollectionChanged event handler above to call InsertOnSubmit on the original Table instance, so you can call SubmitChanges() when convenient. Obviously, you'd probably want to do the same with Remove ...
Hope that helps :)
It may be worth looking into the MVVM pattern. In MVVM you have a ViewModel which wraps your Model, so you would have a ClientTypeViewModel class.
public class ClientTypeViewModel : INotifyProperyChanged
{
public ClientTypeViewModel(ClientType dataModel)
{
this.dataModel = dataModel;
}
public string Description
{
get { return this.dataModel.Description; }
set
{
this.dataModel.Description = value;
// Raise PropertyChanged event
}
}
private ClientType dataModel;
}
And something like an ApplicationView model, which would contain an ObservableCollection of ClientTypeViewModels.
public ApplicationViewModel
{
public ObservableCollection<ClientTypeViewModel> ClientTypes { get; private set; }
}
You then bind to ApplicationViewModel.ClientTypes instead of the plain data model. This way, your view will be automatically updated whenever a new item is added to ClientTypes, or a property is changed on the ClientType view model. ApplicationViewModel can listen for changes on the ClientTypes collection and automatically add newly added items to the DataContext.
You may think it's overkill for your application, I don't know - but MVVM is definitely somthing worth learning. If it feels like you're struglling or fighting with WPF, MVVM is likely where to look ;)
Look at CreateBindingList.
I think it's just because you're assigning the same reference to the DataContext. Hence, WPF doesn't see the need to refresh the binding. The easiest way around this is to:
// rebind
this.DataContext = null;
this.DataContext = db.ClientTypes;

Resources