Declarative progress bar binding - wpf

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.

Related

WPF: How to trigger GUI behaviours in response to view-model events?

I'm developing a WPF/MVVM application and I have a listbox binding to data in a ViewModel. At various points I need the view model to cause the listbox to scroll to a given element.
How can I do this without creating a custom control and while still maintaining good separation of concerns?
I've currently got it working by creating a custom behavior class in the view layer with a dependency property VisibleIndex which the XAML code then binds to an integer in the view model:
<ListBox x:Name="myListBox"
local:ListBoxVisibilityBehavior.VisibleIndex="{Binding VisibleIndex}">
When the integer is set it triggers the dependency properties update handler which tells the listbox to scroll to the associated index.
This seems a bit hacky though because the dependency property value is never changed by the listbox and the update handler only gets called when the value changes, so the only way to ensure that the relevent item is visible is to do something like this:
// view-model code
this.VisibleIndex = -1;
this.VisibleIndex = 10;
The only reason I'm using a behaviour class at the moment is for binding my custom dependency property, is there a way to do something like this with events instead?
Attached properties are somewhat required in your case - as at some point, 'somewhere' you need to call the following method...
ListBox.ScrollIntoView(item)
or
ListBoxItem.BringIntoView();
And for that you need some sort of code behind - and attached properties/behaviors are a nice way of packaging that, w/o impacting your MVVM.
Having said that - if you just need to have your 'selected item' scrolled into view at all times (which is the case most of the time). Then you could use a different attached-property based solution (that again):
mvvm how to make a list view auto scroll to a new Item in a list view
All you have to do then is to set or bind to SelectedItem.
That's a bit 'nicer' if you wish - but the mechanism is the same.
For anyone else interested in the answer to this one of the MS engineers on the WPF forum cleared it up for me. Instead of binding to an event directly you bind to a wrapper object that encapsulates that event. The behaviour can then grab the reference to the wrapper from its DP and do whatever it wants with it i.e. subscribe to the event, trigger it etc.

How to make a contained control in a view to do operation by ViewModel triger?

I have a view that containing a user control.
I want to make the view model to notify the user control to do some action (refresh its' data).
I can pace some bool property in my VM and bind it to the user control so it will trigger it, but I think it's a little abuse of the property.
I feel I missing something, and can't find a solution. I will appreciate any comment.
My solution:
I'm going to solve the problem by registering an event of collection changed in my UserControl, since I'm binding to that control a view of a collection thru CollectionViewSource.
My original problem was how to make a chart control inside the UserControl to get updated when I filtering the data source. After the filtering operation from the VM, an event will raise and I will make the chart to get refresh either in the UserContol's code behind
Since you've indicated MVVM tag solution would be pretty straightforward - just bind control to a data by exposing data items/whatever by ViewModel so any data updates would be automatically dispatched to View via bndings if you would provide INotifyPropertyChanged / INotifyCollectionChanged by a ViewModel.
If you've bound your UserControl correctly, you shouldn't need to manually refresh the data since WPF will automatically update the UI when INotifiyPropertyChanged triggers
That said, if you wish to send a message from the ViewModel to the View, I usually use an messaging system of some kind to keep the Views and ViewModels separate. The two I've used and would recommend are MVVM Light's Messenger, or Microsoft Prism's EventAggregator
Either system will allow your ViewModel to broadcast a message, and your View can subscribe to receive these messages and handle them as needed.

How to call a user control method using MVVM?

I'm working in a WPF project, I'm using the MVVM patter in my project.
I created a user control (also in WPF) and I want to use it in my project, now, my problem is that I have a method in my user control that I need to call from my View Model, but I don't know how to do that, how to bind to the method inside my control from the view model.
If I use code behind, obviously there is no problem since I have a direct reference to my control, so I can do "mycontrol.MyMethod();"m, but of course, doing in this way will go against the logic of the MVVM pattern.
I tried to use a Dependency Property in my user control, and use that Dependency Property to bind to it in the xaml of my project but it didn't worked, the compiler says that the property was not found or is not serializable.
So I will appreciate if someone can share some light about how can I accomplish this.
Edited
As far as I understand you have the view, which is all the GUI, then you have the model, which is all the logic, and them you have the view-model which is like an intermediate layer used to bind the view with the model, right?
In this way I have developed my project, however I came to the problem that I need a custom control, a TextBox that remember what the user entered, and when he start typing, if there are words that start with that letter, those words are shown as a suggestion, as Google does it.
This TextBox is used as a search filter; so I created a user control to do this, I added a method to my user control to allow whatever application that uses my control to add items to an internal array that holds all the strings that the user has entered.
I created a user control because I couldn't find any control that behaves the way I want.
So my problem is when I add my user control to the main project, because I need to someway be able to call the method that add the items to the internal array, but maybe I'm doing things the wrong way, so if any of you has a better idea, I will appreciate if you shared it with me.
You should never call View methods from ViewModel, and vice versa.
Make a property (ObservableCollection?) on your ViewModel, it will have CollectionChanged event, subscribe to it to monitor changes (if needed).
When you add an item to the collection in your ViewModel, GUI will be updated accordingly (you have to perform the Add() operation on GUI thread, btw).
If you need to change the current position in your list, there are colections for that (CollectionViewSource, etc).
If you really really need to pass a string to your control, make a DependencyProperty and bind it OneWay to your ViewModel's property. When you set the value, it will call PropertyChangedCallback on your DependencyProperty.
Why does the consumer of the user control need to maintain the control's internal array? That seems like you've exposed an implementation detail that you don't need to.
Why not simply make that array a dependency property (and an IEnumerable<string> or ObservableCollection<string> besides)? Then you can simply create the corresponding property in your view model and bind it to the control. It also makes the control considerably more versatile.
You shouldn't call something in the View from the ViewModel since that breaks the model.
If the reason you want to call the method in the user control is to do with UI only, I don't see anything wrong with doing it from the view - the view's cs and the view's xaml are in the same "space" in the model. You can be overly-purist in wanting to have lean and mean view cs files.

Bind Winform control property to a property on User Control using INotifyPropertyChanged

Please advise me. Winforms app, C#. I have a user control (UC) that contains a DataGridView.
Firstly, I have a boolean public property in the UC called "IsComplete". in the RowEnter event of my DGV, Im able to set the property accordingly.
Secondly, I successfully instantiate and load this UC into its designated area in my Main form.
PROBLEM:
I would like the 'Enabled' property of a control in my Main form to respond to the IsComplete property in my UC automatically when it changes.
Is this possible? All my searches on Google refer to examples using INotifyPropertyChanged, but only on the same class.
I would certainly appreciate any help on this. TIA!
-cheers!
Why you don't fire off an event when your IsComplete property gets set? This sounds like a pretty trivial thing to do with either events or delegates in c#?!
If you have more complex sort of requirements you should look into the the observer desgin pattern here and here.
It basically allows you to register listener objects to whatever object that changes its states and then action accordingly.

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