F# Event driven MVVM using WPF - wpf

I've been working with Event driven MVVM for a couple of weeks (first time using a design pattern in F#) and I like the idea of separating view and model and also the "functional" controller. But when going through a book on WPF I get the feeling it would be easier if I could adress events directly. Also in some situations I need to get a hold on a control from code behind.
More specific:
How to close a window defined as usercontrol in a XAML file
It seems there would be less need for buttons (triggering booleans that hold state), with a more automated feel as result, if I could directly adress events
Does anybody share this experience or am I still missing something? Is it advisable to go back to FsXaml or polyglot MVVM?

Turns out the code is actually very easy to extend. Based on demos I was able to turn a textbox into a numeric one. The code that does this is very basic, but my intent was to define a custom event accessor. Which can be done by:
Extend UserControl.xaml header with:
xmlns:fsxaml="http://github.com/fsprojects/FsXaml"
fsxaml:ViewController.Custom="{x:Type views:CompositionUserControl}"
And replace the original code in UserControl.xaml.fs:
namespace Space.Views
open FsXaml
type UserView = XAML<"View/UserControl.xaml", true>
type CompositionUserControl () =
member __.ViewModel = Space.ViewModels.UserControlViewModel(Space.Models.Handling.proces)
with
namespace Space.Views
open FsXaml
open System
type UserView = XAML<"View/UserControl.xaml", true>
type CompositionUserControl () =
inherit UserControlViewController<UserView>()
let numeric (txt : string) =
try txt |> int with
| :? System.FormatException -> 0
| _ -> 1
override this.OnLoaded view =
view.Box.PreviewTextInput.Add(fun e -> if numeric e.Text = 0 then e.Handled <- true)
member __.ViewModel = Space.ViewModels.UserControlViewModel(Space.Models.Handling.proces)
EDIT
Looking back at this post, here's my progress regarding my initial questions:
How to close a window defined as usercontrol in a XAML file
Using an attached property DialogCloser.
It seems there would be less need for buttons (triggering booleans
that hold state), with a more automated feel as result, if I could
directly adress events
The key here is to learn:
How to truly separate View(Model) from Model
How to use XAML to exploit its full power

Related

Declarative progress bar binding

After binding the command of a button to an action, I call an object which exposes a progress event.
event System.EventHandler<ProgressChangedEventArgs> ProgressChanged
I would like to display that in my XAML in the best way.
One way can be to expose two bindable fields in my VM
member x.Iteration with get() = _iteration
and set(v:int) = _iteration <- v
x.NotifyPropertyChanged <#this.Iteration#>
member x.IterationVisible with get() = _iterationVisible
and set(v:bool) = _iterationVisible <- v
x.NotifyPropertyChanged <#this.IterationVisible#>
then where I am called to perform the action I would just update the properties
member x.CompleteInference(algorithm:IGeneratedAlgorithm) =
x.IterationVisible <- true
algorithm.ProgressChanged.Add(fun args -> x.Iteration <- args.Iteration)
algorithm.run()
x.IterationVisible <- false
That leads to 2 questions :
Is there a direct way in F# to expose the progressChanged event, without going through this intermediate Iteration property, that can be processed by WPF ? Is it the most declarative we can get / do we always have to store some state somewhere ?
Additionaly, is there a natural way to do this 'state machine' binding entirely in XAML ?
To my knowledge there is no way to handle events in xaml, so exposing the event changes through a property is probably the best you can do.
To achieve "'state machine' binding" in xaml, you could expose the progress as a size and then bind the width of your progress bar to that property. See here for an example of this.
You look to be doing this correctly although I am new to F#.
Wpf is typically used with an MVVM pattern, so wiring your button to a command property and your progress bar to an int property is correct. Your command then does something to cause the progress value to change and bindings take care of the rest.
Handling events in xaml requires code behind, which personally I don't mind if it serves only to enhance the UI. It certainly shouldn't be interacting with other objects. That means no event handlers.
This brings in event to command behaviours which can be used to bind control events to commands. See msdn.
Hope this helps in some way.

Access windows by name

I am quite new to WPF, coming from the Delphi world. I solved the problem below (albeit painfully) in the Delphi world, and hope there is a more elegant solution in the WPF world.
I need to read in an XML file containing a menu "tree", which has the window names in it as well as the menu prompts, and then be able to "show" a window based on having its name.
For example, a segment of the menu, with two choices, might have XML like this:
<MenuLeaf>
<Header>Product information</Header>
<MenuLine>
<Prompt>Product Master File</Prompt>
<WindowName>Products.xaml</WindowName>
</MenuLine>
<MenuLine>
<Prompt>Inventory Data</Prompt>
<WindowName>Inventory.xaml</WindowName>
</MenuLine>
</MenuLeaf>
So when the user makes the "Inventory Data" choice, I will know that I want to do a "show" of the window Inventory.xaml ..... but I only have the literal string "Inventory.xaml".
I will have hundreds of these forms, and the XML file can vary from time to time - so it's not effective for me to have the standard code of
Dim window as New Inventory
window.Show
for each of the several hundred windows.
What I need is something that does
Dim window as New {go out and find the Inventory file with name Inventory.xaml}
window.Show
I have searched endlessly for this with no luck.
I think the path to solution is to use Reflection, which will allow you to dynamically find/invoke your classes. Say your Namespace is MyNs, then you must have a 'Products' Class within it that correspond to the 'Products.xaml' file. To find it, use MyFoundType = MyNs.GetType("Products")
Then get default (or other if you like) constructor for this type : MyFoundType.GetConstructor(). Then invoke the constructor (with arguments if needed) --> you now have your window as an Object.
Cast it to a window and call its Show method, and you're done.
http://msdn.microsoft.com/en-us/library/y0cd10tb.aspx
http://msdn.microsoft.com/en-us/library/h93ya84h.aspx
http://msdn.microsoft.com/en-us/library/6ycw1y17.aspx
You need to use the XamlReader object, which parses XAML at run-time and creates the object.
var rdr = XmlReader.Create(File.Open("Inventory.xaml"));
var window = XamlReader.Load(rdr) as Window;
window.Show();
The XamlReader.Load will return whatever the actual top-level element in the XAML specifies; if it's a Window you can just .Show it. If it's something else, you'll need a container to place it in. For example, you might have a Window with a Border element in it and do:
var control = XamlReader.Load(rdr) as UserControl;
var window = new MyHostWindow();
window.ContentBorder.Child = control;
If you don't actually know the type of element in your XAML you can usually use FrameworkElement, which is the base class for all the visual elements, though you won't get Window-specific behavior from that.

WPF: Managing windows (opening, closing, etc...) in MVVM?

I've read about it in a bunch of places. Most of the people are referring to these two links:
How do I handle opening and closing new Windows with MVVM?
http://waf.codeplex.com/
I don't understand either of them. I am a beginner when it comes to MVVM. Some people are mentioning controllers when it comes to window manipulation in MVVM. What are those and how are they implemented? By book, MVVM is consisted of model, viewmodel and view - where do controllers come in?
If someone could provide a sample of the following use case, that would be terrific (for all those people who are just getting started with this, as I am):
Prerequisite: A window is opened.
User clicks a button.
New window is opened and some data is passed to that window, i.e. some string.
New window is closed (or a button is clicked) and some data is passed to the first
window.
Passed data has changed something on the window.
The ViewModel to ViewModel communication is usually handled by an implementation of the Event Aggregator pattern.
MVVM Light uses the Messenger class, Prism has another implementation but basically that is one way to send messages between View Models without coupling.
There are some examples, and Articles describing the usage.
I would suggest taking a look at it.
Regarding the controllers stuff in WPF, I don't know.
Regarding the example:
-I Have a Windows with its WindowsViewModel. This class should have a Command bound to the Button.
-The user clicks the button. Executes the command.
-The Command opens a new Window.
Here you should create the Dialog View Model and somehow you should create the Window. Or create the Window with a ViewModel, but ViewModel shouldn't know much about the View otherwise is not testable.
We use something like this because we have some requirements, but it
could be much much simplier, it happens it's the only example I have at hand:
bool? ShowDialogImpl<TViewModel>(Action<TViewModel> setup) where TViewModel : ViewModel
{
return (bool?)DispatcherHelper.UIDispatcher.Invoke(
(Func<bool?>)(() =>
{
var viewModel = viewModelFactory.Get<TViewModel>();
viewModel.ViewService = this;
setup(viewModel);
var window = new Window
{
Owner = this,
SizeToContent = SizeToContent.WidthAndHeight,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Content = ViewFactory.CreateView<TViewModel>(),
DataContext = viewModel,
WindowStyle = WindowStyle.ToolWindow,
ShowInTaskbar = false
};
window.SetBinding(TitleProperty, new Binding("Title"));
openDialogs.Push(window);
window.Activated += (sender, args) => window.SizeToContent = SizeToContent.Manual;
var result = window.ShowDialog();
openDialogs.Pop();
viewModelFactory.Release(viewModel);
return result;
}));
}
Basically: We create a window and with a view model.
The view model is created from a factory using an container.
The setup Action delegate is the entry point for our data.
Communication:
The first Windows is a Grid, and the second Dialog to edit data of the grid.
Inf the Windows we have:
messenger.Register<EntityUpdated<FooClass>>(this, message => UpdateItem(message.Entity));
And in the Dialog:
messenger.Send(new EntityUpdated<FooClass>(subject));
This way, we know when something was updated in the Edit Dialog in order to refresh the grid.
Hope this help you :)
If you aren't planning on allowing the user to switch back and forth between the windows, while both are open (i.e., the first opens the second, and the second must be closed to return to the first), you could simply set the viewmodel for both windows to the same viewmodel instance, and open the 2nd window as modal, and the strings you are passing back and forth, would simply be properties of the view model, with data bindings to something in the view.

How do I open a WPF window in AddNew mode before any data has been loaded?

I want to open a WPF4/EF4 form in AddNew mode so the user can start entering data in bound controls before any data has been selected from the database. I already have an "Add New Record" button but it only works with a populated DataContext (my CollectionViewSource). Here is the code so far:
private void btnAddNewRecord_Click(object sender, RoutedEventArgs e)
{
LabSample newEntity = _labEntitiesContext.LabSamples.CreateObject<LabSample>();
_labEntitiesContext.LabSamples.AddObject(newEntity);
_labSamplesListCollectionView.AddNewItem(newEntity);
}
Background: This is a basic WPF app with bound controls. I started with an Entity Framework model that appears in my DataSources window. I dragged my LabSample entity from the DataSources window and let it create my CollectionViewSource (labSamplesViewSource) in the XAML's Windows.Resources section. The DataContext for all my controls is the labSamplesViewSource. I create a new LabEntities object called _labEntitiesContext as the window is instantiated. I use _labEntitiesContext to build my filtered ObjectQuery(of LabSample) and to SaveChanges, but I'm a little confused as to how this _labEntitiesContext is hooked up to my CollectionViewSource. If you could clarify this along with answering my question that would be helpful. Note: I'm not ready to use MVVM.
When the window loads I use this.FindResource to grab a reference to the CollectionViewSource in a class level variable named _labSamplesCollectionViewSource. I allow the user to enter search fields to populate the screen with data. My LoadData routine looks something like this:
System.Data.Objects.ObjectQuery<LabSample> labSamplesObjectQuery = this.GetLabSamplesFiltered_Query(_labEntitiesContext, sampleID_LIKE, xxx_LIKE, yyy_LIKE);
System.Data.Objects.ObjectResult<LabSample> labSamplesObjectResult = labSamplesObjectQuery.Execute(System.Data.Objects.MergeOption.AppendOnly);
_labSamplesCollectionViewSource.Source = new System.Collections.ObjectModel.ObservableCollection<LabSample>(labSamplesObjectResult);
_labSamplesListCollectionView = (ListCollectionView)_labSamplesCollectionViewSource.View;
The _labSamplesListCollectionView class level variable set above is used in my btnAddNewRecord_Click code. Before LoadData is called the _labSamplesListCollectionView is null causing my AddNew code to fail with "Object reference not set to an instance of an object".
How can I make this work? I'm wondering if I should be making use of _labSamplesListCollectionView.AddNew instead of my current technique but I couldn't get that work either. Your help will be greatly appreciated.
I am writing an app that does something similar. I am however using MVVM pattern, which allows me to do some neat things. In mine, I am working with Shipments. On the ShipmentsView, I can click an "Add New" button which fires off a bound command property which is housed in the associated ViewModel class. That command methods looks like the following: Note: Views in this context are not CollectionView but refer to MVVM View classes.
Dim NewShipment = New Shipment()
_Context.AddToShipments(NewShipment)
Dim ShipVM = New ShipmentViewModel(NewShipment)
ShipmentVMCollection.Add(ShipVM)
Dim NewShipmentView as ShipmentView(ShipVM)
My ShipmentView handles it's placement and visiblility, and my Shipment object has it's property values initialized so that it does not immediately present errors via it's validation handlers. This way the user can create a new shipment and if they get sidetracked they can save it and come back to it without having a bunch of mandatory fields.
When I use a CollectionViewSource, I populate it with an ObservableCollection of my entities, and then add the entities to that observable collection when I create them. ObservableCollection implements INotifyPropertyChanged and INotifyCollectionChanged events and notifies the UI when something happens, and it all works through the CollectionViewSource.
You might take a look at the MVVM pattern which is really good for moving data and keeping it in the proper scope, and there are some good MVVM frameworks out there that will help you make a nice application with MVVM.
MVVM may be a bit of overkill for your app if it is small. But if it gets over more than just a few Views it is going to get unwieldy and hard to move data back and forth and keep it current, and maintainable.
Wiki Article for MVVM - a pretty good place to start and get links
This is my Constructor for one of my ViewModels. I realize you don't want to implement in MVVM right now, but a code behind would be similar. In this instance, I am using a background worker to get my entity records, (the constructor call be for that and the View setting immediately afterward can be disregarded), then I link up my CVS, Populate it with my ObservableCollection, and set it's View to a field so I can filter on it later.
Public Sub New(ByRef MyView As NTMsView)
Me.New(ViewManagerService.CreateInstance, ViewModelUIService.CreateInstance)
NTMsBackgroundWorker.RunWorkerAsync()
_View = MyView
_NTMCollectionViewSource = _View.FindResource("NTMCollectionViewSource")
_NTMCollectionViewSource.Source = NTMs
_NTMCollectionView = _NTMCollectionViewSource.View
End Sub
This is an example of my AddRecord method. Then I instance a new object, add it to the appropriate collection in the Context, Save it, execute a stored procedure, then refresh the context since the stored procedure did a few things to the record. Then I add the object to my Observable.
Private Sub AddNTM()
'Create an NTM Object.
Dim NewNTM As New NTMShipment()
'Add it to the context
_context.AddToNTMShipments(NewNTM)
_context.SaveChanges()
_context.MakeNewSecurityID(NewNTM.NTMShipID)
_context.Refresh(RefreshMode.StoreWins, NewNTM)
'Wrap it in a ViewModel and Add it to the NTMs collection
NTMs.Add(New NTMViewModel(NewNTM))
End Sub
As for creating a new entity before your CollectionViewSource is created, a couple of questions. Is your edit forms datacontext related to the CVS? In my forms, the CVS is only used in conjunction with ItemsControls since it is displaying a collection of items. If your edit forms controls are dissociated from the CVS, you should not have much trouble populating them with a new entity and when it comes time to save, check to see if CVS is null and if so, create it then populate it.
If that is not a good answer, could you expand on how your application is structured?
Instead of opening the Window in AddNew mode I disable all data entry controls when the window loads or the when a search returns no records. When the "Add New Record" button is clicked I ALWAYS start over with a new data context that contains just one new entity. This means I have to prompt to save changes if any dirty (modified) records exist. The prompt allows the user to save changes, discard changes or continue editing (never entering AddNew mode). Here is the AddNew code:
MessageBoxResult response = PromptToSaveChanges(ReasonForPromptToSave.LoadingData);
if (response == MessageBoxResult.Cancel) return;
LabSample newEntity = _labEntitiesContext.LabSamples.CreateObject<LabSample>();
_labEntitiesContext.LabSamples.AddObject(newEntity);
_labSamplesCollectionViewSource.Source = new ObservableCollection<LabSample>();
_labSamplesListCollectionView = (ListCollectionView)_labSamplesCollectionViewSource.View;
_labSamplesListCollectionView.AddNewItem(newEntity);
_labSamplesListCollectionView.CommitNew();
_labSamplesListCollectionView.Refresh();
Here are my steps to put the window in AddNew mode:
1) Prompt to save changes.
2) Create a new entity and add it to my data context.
3) Create a new ObservableCollection of my entity type and assign it to the .Source of my CollectionViewSource. Note the _labSamplesCollectionViewSource is a reference to the XAML's CollectionViewSource that was auto-generated by dragging a table from the data sources window.
4) Assign the .View of the CollectionViewSource to a class level ListCollectionView variable.
5) Add the new entity to the ListCollectionView that was just created.
6) Call CommitNew and Refresh on the ListCollectionView
I have answered my own question here, but keep in mind that the answer is the result of trial and error and may not be ideal. Regarding my confusion as to how the _labEntitiesContext is hooked up to the CollectionViewSource I believe the answer is in the line that reads _labSamplesListCollectionView.AddNewItem(newEntity), but I'd like to see an explanation of how all of the objects reference each other.
My final comment/question is that I'm disappointed at how hard it is to find a standard reference application or document that teaches non-MVVM WPF/Entity Framework databinding in detail. Microsoft promotes drag-and-drop binding but leaves us without a reference on how to build a complete application. I'll move on to MVVM soon, meanwhile if anyone can direct me to a GREAT resource or feature complete application that is WPF, non-MVVM and Entity Framework I would greatly appreciate it.

Open dialog in WPF MVVM

I have an application that need to open a dialog from a button where the user enters some information.
At the moment I do it like this (which works fine)
The button click generates a command in the ViewModel.
The ViewModel raises an event which the Controller listens to.
The Controller works out the details of the new window (i.e. View, ViewModel & model) and opens it (ShowDialog)
When the window is closed the Controller adds the result to the eventargs and returns to the ViewModel
The ViewModel passes the information to the Model.
There are a lot of steps but they all make sense and there is not much typing.
The code looks like this (the window asks for the user's name)
ViewModel:
AskUserNameCommand = DelegateCommand(AskUserNameExecute);
...
public event EventHandler<AskUserEventArgs> AskUserName;
void AskUserNameExecute(object arg) {
var e = new AskUserNameEventArgs();
AskUserName(this, e);
mModel.SetUserName(e.UserName);
}
Controller:
mViewModel.AskUserName += (sender,e) => {
var view = container.Resolve<IAskUserNameView>();
var model = container.Resolve<IAskUserNameModel>();
var viewmodel = container.Resolve<IAskUserNameViewModel>(view, model);
if (dlg.ShowDialog() ?? false)
e.UserName = model.UserName;
}
My question is how the horizontal communication works in the MVVM pattern.
Somehow it seems wrong to let the controller be involved in the data transfer between the models.
I have looked at the mediator pattern to let the models communicate directly. Don't like that idea since it makes the model depending on implemetations details of the GUI. (i.e. if the dialog is replaced with a textbox, the model need to change)
I don't like most of the current suggestions for one reason or another, so I thought I would link to a nearly identical question with answers I do like:
Open File Dialog MVVM
Specifically the answer by Cameron MacFarland is exactly what I do. A service provided via an interface to provide IO and/or user interaction is the way to go here, for the following reasons:
It is testable
It abstracts away the implementation of any dialogs so that your strategy for handling these types of things can be changed without affecting constituent code
Does not rely on any communication patterns. A lot of suggestions you see out there rely on a mediator, like the Event Aggregator. These solutions rely on implementing two-way communication with partners on the other side of the mediator, which is both hard to implement and a very loose contract.
ViewModels remain autonomous. I, like you, don't feel right given communication between the controller and the ViewModel. The ViewModel should remain autonomous if for no other reason that this eases testability.
Hope this helps.
i use this approach for dialogs with mvvm.
all i have do do now is call the following from my viewmodel to work with a dialog.
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
I have come across similar problems. Here is how I have solved them, and why I have done what I have done.
My solution:
My MainWindowViewModel has a property of type ModalViewModelBase called Modal.
If my code needs a certain view to be modal, it puts a reference to it in this property. The MainWindowView watches this property through the INotifyPropertyChanged mechanism. If Modal is set to some VM, the MainWindowView class will take the VM and put it in a ModalView window where the appropriate UserControl will be shown through the magic of DataTemplates, the window is shown using ShowDialog. ModalViewModelBase has a property for DialogResult and a property called IsFinished. When IsFinished is set to true by the modal VM, the view closes.
I also have some special tricks for doing interactive things like this from backgroundworker threads that want to ask the user for input.
My reasoning:
The principle of modal views is that other views are disabled, while the modal is shown. This is a part of the logic of the View that is essentially lookless. That's why I have a property for it in the MainWindowViewModel. It I were to take it further, I should make every other property or command for all other VM's in the Main VM throw exceptions, while in modal mode, but I feel this to be excessive.
The View mechanism of actually denying the user any other actions, does not have to be performed with a popup window and showdialog, it could be that you put the modal view in the existing window, but disable all others, or some other thing. This view-related logic belongs in the view itself. (That a typical designer can't code for this logic, seems a secondary concern. We all need help some times.)
So that's how I have done it. I offer it only as a suggestion, there is probably other ways of thinking about it, and I hope you get more replies too.
I've used EventAggregator from Prism v2 in similar scenarios. Good thing about prims is that, you don't have to use entire framework in your MVVM application. You can extract EventAggregator functionality and use it along with your current setup.
You might have a look at this MVVM article. It describes how a controller can communicate with the ViewModel:
http://waf.codeplex.com/wikipage?title=Model-View-ViewModel%20Pattern&ProjectName=waf

Resources