Update Label (WPF) During Runtime [duplicate] - c#

I'm a web and backend programmer by nature. Normally I try to avaoid making windows programs. Now I have to make a WPF client.
I have a background task that raises an event every often time. (It is working like a poller and when the criteria are met an event is raised). Noob as I am I wrote this code that was attached to the event to update the UI.
private void IsDisconnectedEvent()
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
}
This gives an exception because I am not on the same thread. After some googling I found that I should change the code with:
private void IsDisconnectedEvent()
{
Dispatcher.Invoke(() =>
{
UserWindow.Visibility = Visibility.Hidden;
DisconnectWindow.Visibility = Visibility.Visible;
});
}
This works, but this is not the only event and thus makes my code horrible ugly. Are there better ways to do this?

Regarding this:
This works, but this is not the only event and thus makes my code
horrible ugly
Yes, your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality.
Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of Declarative DataBinding as opposed to the traditional imperative approach.
This means that there should be nothing like this:
UserWindow.Visibility = Visibility.Hidden;
anywhere in your code, simply because introducing things like that makes your code dependent on the UI and thus only executable on the UI thread.
Instead, the WPF approach to that would be to declaratively DataBind the Visibility propety of the UI element (IN XAML) to a relevant bool property that you can operate from the outside, like this:
<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
<!-- ... -->
</UserWindow>
Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to. This is called a ViewModel.
Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface.
When doing so, it is also convenient to have the PropertyChanged event from that interface marshalled to the UI thread, so that you no longer have to worry about setting the ViewModel's properties by using the Dispatcher.
Therefore our first step is to have all our ViewModels inherit from a class like this:
(taken from this answer):
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
//Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter:
Application.Current.Dispatcher.BeginInvoke((Action) (() =>
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}));
}
}
Once we have our Property Change Notification Dispatch to the UI Thread in place, we can proceed to create a relevant ViewModel that suits, in this case, the UserWindow and it's DataBinding expectations:
public class UserViewModel: PropertyChangedBase
{
private bool _showUserWindow;
public bool ShowUserWindow
{
get {return _showUserWindow; }
set
{
_showUserWindow = value;
OnPropertyChanged("ShowUserWindow"); //This is important!!!
}
}
}
Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel. One simple way to do that is in the Window's constructor:
public UserWindow() //Window's Constructor
{
InitializeComponent(); //this is required.
DataContext = new UserViewModel(); //here we set the DataContext
}
As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code. This is good not only because it resolves the Thread Affinity issues (because now you can set the ShowUserWindow property from any thread), but also because it makes your ViewModels and logic completely decoupled from the UI and thus testable and more scalable.
This same concept applies to EVERYTHING in WPF.
One detail that I need to mention is that I'm making use of a technique of Combining MarkupExtension and IValueConverter in order to reduce the the XAML boilerplate involved in using Converters.
You can read more about that in the link and also the MSDN DataBinding page linked above.
Let me know if you need further details.

Related

Provide "trigger-method" to module-(sub)-view model in WPF and MVVM

I'm got an other question for my WPF/MVVM application I'm working on since a while.
The main idea is to use a main window providing a navigation bar and a ContentControl.
The different "Modules" are all built as UserControl with each its own ViewModel.
The main call from the main viewmodel to start a module is
private void ShowAddressModule() {
ContentControlBindingProperty = new AddressModule(new AddressModuleViewModel);
}
In the real application the viewmodels are pre-loaded and so on, but the start is more or less the same.
The main view model contains a boolean property LongRunningOperation to do multiple operations on the main window while any long running operation.
As example showing a loading image or disable the main navigation while loading a new module or whatever.
So my idea is to provide a possibility to the modules (their view models) to active this "mode".
Example how it could look in the modules view model:
private void LoadContactList() {
MainWindow.LongRunningOperation = true;
LoadAllContactsInAThread(); /*Takes a long time*/
MainWindow.LongRunningOperation = false;
}
I tried to mark the property as static and public, but this will not work because of the OnPropertyChanged event.
If possible it would be great if the solution could be applied also to methods (including parameters) from the main window - so (as example) the modules could use as example the parents statusbar or so.
MainWindow.ShowErrorMessageInStatusBar("The error xyz occured!");
Hopefully I described good enought, what's my idea...
And hopefully anybody could provide me the needed tip how to handle this requirement.
Thanks in advance for any hints
Regards Markus
Each module could raise an event to indicate the start of a long running operation, and raise an event to indicate the end of a long running operation. Your main view model, when loading modules for the first time, could hook-up to these events and react to them accordingly.
Your sub view model would have some events like this:
Sub view model
public delegate void OnLongRunningOperationStartedEventHandler(object sender);
public delegate void OnLongRunningOperationFinishedEventHandler(object sender);
public event OnLongRunningOperationStartedEventHandler OnLongRunningOperationStarted;
public event OnLongRunningOperationFinishedEventHandler OnLongRunningOperationFinished;
private void LoadContactList() {
OnLongRunningOperationStarted?.Invoke(this);
LoadAllContactsInAThread(); /*Takes a long time*/
OnLongRunningOperationFinished.Invoke(this);
}
And your main view model will hook-up to them like this:
Main View Model
public bool LongRunningOperation { get; private set; }
// Keep track of the number of modules currently running long operations
private int _countLongRunningOperations = 0;
public LoadSubModules(){
// Depending on how you load your sub modules, this piece of code could move around
foreach (var module in submodules){
module.OnLongRunningOperationStarted += Module_LongOperationStarted;
module.OnLongRunningOperationFinished += Module_LongOperationFinished;
}
}
private void Module_LongOperationStarted(object sender){
_countLongRunningOperations += 1;
LongRunningOperation = true;
}
private void Module_LongOperationFinished(object sender){
_countLongRunningOperations -= 1;
if (_countLongRunningOperations == 0) {
LongRunningOperation = false;
}
The same principle (using events) could be used to bubble up error messages from each submodule to the main view model.
The quick and very dirty approach:
Grab a reference to mainwindow out of application.current.mainwindow. Cast it to MainWindow. It's a property set to whatever the first window you show is - MainWindow just happens to be the default name of the main window.
You can then set the property on that if it's a public dependency property. Make sure the dp binds twoway in it's metadata.
This is bad because you're referencing ui in your viewmodels and you have no application when you run tests on viewmodels in some test runner.
The quick and dirty approach
Add a public property to app and set this to your instance of mainwindowviewmodel in it's ctor. You can reference app from an piece of code. Add a public property to mainwindowviewmodel and bind to that.
This is bad because you have no application when you run tests on viewmodels in some test runner.
You could add a static with an interface abstracts this away and work round that though.
My suggestion
This hinges on the fact you can use dot notation to bind and that includes
Content.IsBusy on yourcontentcontrol.
You can therefore bind from a parent window to a dependency property of any usercontrol that happens to be in it's contentcontrol.
Add that property using an attached property and bind that to IsBusy in a base viewmodel. Inherit the viewmodels of your child views from that.
One thing to mention is that binding to an attached property is a little odd and rather than just
ElementName=YourContentControl, Path=Content.YourAttachedProperty
You need something like:
ElementName=YourContentControl, Path=Content.(local:AttachClass.YourAttachedProperty)

Multiple views sharing same data with two-way data binding between multiple threads

UWP app ( mvvm architecture ) I have a MainView which has a collection in its ViewModel, used to bind to the GridView on MainView and each item has a TextBox with 2 way databinding with Description property of class Note.
Xaml of the TextBox of each gridviewitem.
<TextBlock Text="{x:Bind Description,Mode=TwoWay}"
Collection property used to bind to ItemSource of gridview.
public ObservableCollection<Note> Notes { get; }
and this is the class Note
public class Note : Observable
{
private string _description;
public string Description
{
get => _description;
set => Set(ref _description, value, nameof(Description));
}
}
the Observable class is for two way data binding help.
public class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Now everything uptil this point works perfectly, when I change the text in textbox, it changes the value of Description as well.
Second View
Now I have a feature where each GridViewItem has a button in it which opens the Note in new window. and this new window has nothing but only 1 TextBox, so now the secondary view and the GridViewItem which opened that view are using the same object of Note.
This TextBox in secondary view also has 2 way data binding with the Description of the Note.
The Problem
What I want is that whether the textbox in gridview or the textbox on the secondary view is edited, the value of description must remain synced between these 2 textboxes, that is why I tried to bind them 2 way with same object of Note hence the same Description object is bound to both of them.
Error here was expected to me which was Marshalling threading error, so whenever I try to change value of any textbox, it tried to update UI on other view ( which is another thread ) which is ofcourse not allowed.
I know about CoreDisptcher
I already know about the Dispatcher feature of UWP for safe cross thread communication, I already have it all setup and if I use it from a normal method I can easily use it for cross thread UI update and it totally works. But my issue is the following line :
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\
Exception occurs when it tried to invoke the PropertyChanged I tried to wrap following line in my Dispatcher :
OnPropertyChanged(propertyName);
but INotify interface does not allow me to have a Set<> method which returns a Task instead it needs to return just an object, this is the point where I am stuck and I dont know how to make use of Dispatcher in this scenario, please let me know if there is some better way to do this, it seems this way might not be so efficient.
Thanks.
The best solution in this case would be to have a separate set of INotifyPropertyChanged instances for each window and using some kind of messaging solution like EventHub in MvvmLight, which publishes message that the underlying model changed and all interested parties should update their instances.
Another option would be to create a base model class, which maintains a dictionary of INotifyPropertyChanged instances for each UI thread (so it would be a Dictionary<Dispatcher, YourModelClass>. Now the parent would subscribe to PropertyChanged event of each child instance and once it executes would propagate the event to other childs using the appropriate Dispatcher.
Also there is a very interesting utility class ViewSpecificBindableClass by Marian Dolinský on his GitHub which could potentially be a solution that would allow you to have "single" class in multiple views, aware of multiple dispatchers. I haven't tried it yet, but it seems promising.
So I finally had to take a totally different approach centralizing TextChanged events of MainView textbox and the one on the secondaryview.
I essentially passed the textbox on the mainpage through to the secondary page ( secondary view ) and then subscribed to its TextChanged event. I also subscribed to the TextChanged event of textbox on the secondary view, and then with help of reverse dispatchers I was able to sync the text between 2 windows without any problems.
Note : always make sure to unsubscribe to events when the secondary window closes to prevent memory leaks.
private async void PipBox_TextChanged(object sender, TextChangedEventArgs e)
{
string text = PipBox.Text;
await CoreApplication.MainView.Dispatcher.AwaitableRunAsync(() =>
{
if (parentBox.Text != text)
parentBox.Text = text;
});
}
private async void ParentBox_TextChanged(object sender, TextChangedEventArgs e)
{
string text = parentBox.Text;
// the awaitablerunasync extension method comes from "Windows Community Toolkit".
await _viewLifetimeControl.Dispatcher.AwaitableRunAsync(() =>
{
if (ViewModel.MyNote.Description != text)
ViewModel.MyNote.Description = text;
});
}
Notice that I still have 2 way data binding on both textboxes and it does not cause any exceptions because I am using 2 different instances of Note for both views.
<TextBox Text="{x:Bind ViewModel.MyNote.Description, Mode=TwoWay}"
x:Name="PipBox"/>
but because I have twoway data binding on both textboxes, that is how I can easily keep both instances of Note in sync as well on separate threads.
I will keep the github repo in case it can help anyone else : https://github.com/touseefbsb/MultiWindowBindingSync
P.S : A special thanks to Martin Zikmund who helped me a lot in figuring out this solution.

Swapping out the object reference of a bound property using a BackgroundWorker

A little background
I'm building an application which uses a plug-in architecture to access specific devices. These devices have fields, which I named Attributes, that can be read, written or both. This is done using a WPF front-end running in it's own thread and a MVVM like structure in the back end which wraps specific plug-in to expose it's attributes in a generic way.
Some details about the threading structure
I have two "host" objects, one initiates the plug-in structures eventually exposes two views: a view of available plug ins and (if a plug-in was selected) a view of all attributes known by a single plug-in. The other eventually starts a STA-thread and runs the a main window in a WPF application. The second host uses BackgroundWorkers to do select, initialization, update and submit tasks. The DoWork events/delegates(I believe they are called delegates) are defined in a ViewController class which is in the MVVM structure and provides a functional interface for updating etc. and implements the INotifyPropertyChanged interface.
Some more details
Upon creation of the mainwindow it's context is set to the view controller object. The GUI then binds two list boxes to both views.
The problem
When I call the selectPlugin method from within the UI thread it freezes until the connect operation is complete and every single attribute is loaded into the list contained by the ViewModel. However it does work and afterwards the ListBox itemssource binding is updated and all attributes are shown.
However I don't want the UI to freeze up on every operation, so I implemented backgroundworkers. It all works fine, the objects get updated and the binding source is replaced by a new View instance. But the binding itself doesn't update.
I tried allot off different solutions using the Dispatcher.Invoke or the DoWorkComplete event in the UI thread. I found that the PropertyChanged event itself stays null using the following property setter:
public IAttributesViewModel AttributesView
{
get
{
StaticLogger.WriteDebugLog(log,
String.Format("Executing {0}",
System.Reflection.MethodBase.GetCurrentMethod()));
return _currentAttributes;
}
set
{
_currentAttributes = value;
OnPropertyChanged("AttributesView");
}
}
The implementation of INotifyPropertyChanged looks like this:
#region INotifyPropertyChange implementation
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Called when [property changed].
/// </summary>
/// <param name="name">The name.</param>
protected void OnPropertyChanged(string name)
{
StaticLogger.WriteDebugLog(log, String.Format("Executing {0}", System.Reflection.MethodBase.GetCurrentMethod()));
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
The PropertyChanged event as declared above is always null. This could or should have something to do with the actual binding in XAML, however i did set de datacontext of the main window in the UI thread like this:
private static void Host(object viewControler)
{
var controler = viewControler as IViewController;
_instance._main = new MainWindow(controler) { DataContext = controler };
var gui = new App();
if (_instance._main == null) throw new ArgumentNullException("viewControler");
gui.Run(_instance._main);
}
I use a Singleton instance off the UI host object, in case that makes a difference.
In XAML I use a custom dependency property on a UserControll containing the AttributesListbox and some styling code. The actual ListBox binds to this property for it's own ItemSource property. Don't think this should be different from using the list box directly, but just in case this causes the issue with the PropertyChanged event being null, and is implemented in the following way:
C#
///
/// Interaction logic for AttributesListBox.xaml
///
public partial class AttributesListBox : UserControl
{
private static readonly UIPropertyMetadata _sourceMetadata = new UIPropertyMetadata(String.Empty,
SourceChangedCallback);
public AttributesListBox()
{
InitializeComponent();
}
/// <summary>
/// The dependency property that gets or sets the source of the ListBox to render.
/// </summary>
public static DependencyProperty sourceProperty = DependencyProperty.Register(
"Source", typeof(IEnumerable), typeof(AttributesListBox),_sourceMetadata);
/// <summary>
/// Gets or sets the nsource of the image to render.
/// </summary>
public IEnumerable Source
{
get { return (IEnumerable)GetValue(sourceProperty); }
set { SetValue(sourceProperty, value); }
}
private static void SourceChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var box = (AttributesListBox) d;
box.List.ItemsSource = e.NewValue as IEnumerable;
}
}
XAML
<ListBox Name="List" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding Source}" BorderBrush="{x:Null}" />
The first question(s)
What is wrong with the way I use the ViewController object in conjunction with the UI? And thus why is the PropertyChanged event always null? This should mean I messed up the binding process somewhere doesn't it?
The second question(s)
How do I get the OnPropertyChanged event to notify the UI thread, specificity the Binding in the UserControll(AttributesListbox)?
What route does the event follow?
for instance:
I use a DoWork delegate and this method changes the property directly from within the instance, which is not in the UI thread. Does this cause the event never to reach the UI thread because is it raised in the other worker thread?
I can't get my head around the event bubbling/tunnelling, is a event restricted to the thread which created the instance or the thread which called the specific method(using a dispatcher or whatever)?
Hypothesis
I suspect there are two issues in my case:
1. The PropertyChanged handler stays null because the object is either not bound or because it is not created in the correct thread.
2. The event, if it where actually fired, never reaches the correct thread because it gets stuck in either the BackgroundWorker thread or the back end "host" thread.
This is the first question I have asked here, so if your missing some pieces off the puzzle please do inform me.
Thanks for taking the time to read about my little problem situation, I hope we can come to a solution.
I think you may have messed up the binding of your listbox, namely (shortened for simplicity):
<ListBox ItemsSource="{Binding Source}" />
this binding means "look for a property called Source in the my current DataContext", which I suspect is not what you intend. If you want to bind to the property Source of the AttributesListBox (which I suppose hosts the above listbox), you should do it this way:
<ListBox ItemsSource="{Binding Source, RelativeSource={RelativeSource AncestorType={x:Type AttributesListBox}}}" />
which means - "look for a property called Source in the first object up the tree that is of type AttributesListBox". Of cource you still have to bind that Source property to the correct thing of your controller, but I suppose you have done that.
The deal with the property changed event is that it must be raised from the UI (Dispatcher) thread, if you raise it from a background thread wpf will not automatically marshal it to the UI thread. So make sure that when the background work has completed, you set the propoperties which need to be updated on the UI thread.
The great thing about asking a question is that is forces you to really think on de cause off your problem, thus taking a little break you might finally, after two days, find the utterly simple solution.
The problem was a ParameterizedThreadStart which request a void method(object arg) so the compiler does no type checking what so ever as long as you pass a object. An I passed the wrong object because I edited the method. The safe typecasting using the "as" operator seems to be a pain to, it seems to swallow an CastingException thus not informing you about the small mistake.
So simple solution is:
private static void Host(object viewControler)
{
var controler = viewControler as IViewController;
_instance._main = new MainWindow(controler) { DataContext = controler };
var gui = new App();
if (_instance._main == null) throw new ArgumentNullException("viewControler");
gui.Run(controler);
}
I didn't expect my BackgroundWorker to work a 100% "out of the box" but it seems to do so. Whether my code is "best practice" is another separate question.
The key to making me think in that direction was:
Short example: bad and good practice for databinding and object initialization
When working with DataBindings in visaul studio also check out this small article:
How to show WPF trace

Equivalent of PostMessage in C# to synchronize with the main thread with MVVM?

I must be retarded with searching, because here's another seemingly common problem that I haven't been able to solve.
Here's my problem -- I am using WPF and MVVM, and I have a statemachine that executes in the model. If an error occurs, I need to pass information up to the ViewModel to display the error. This part seems to work okay. When the user clicks the desired behavior, the code in the model continues, and looks at the object the user interacts with to determine what to do next.
The problem is that the model needs to reload a file, which updates the GUI with the contents of said file. Because the model is executing in a thread, you can imagine what I'm going to ask next -- how the hell do you synchronize with the GUI properly? In MFC, I would have used either SendMessage or PostMessage to accomplish the GUI update.
I've read articles for WinForms that suggest using InvokeRequired to automatically call BeginInvoke if necessary. I actually didn't know that BeginInvoke would accomplish what I wanted, so I was encouraged to learn this.
How do I actually call BeginInvoke from my model? Does this method even apply to WPF? I went ahead and implemented a delegate and then called Invoke, but I get the same error that tells me the collection can't be modified from this thread. I also tried BeginInvoke for the hell of it, but I assume that also wouldn't work because it would just launch from a different thread anyway.
Confused. If I have missed something really obvious that's been posted about all over the internet, go ahead and give me a verbal lashing, I probably deserve it.
EDIT - I should probably also add that I am looking for something other than a timer or BackgroundWorker-based solution, unless that's the only way to solve this in WPF / MVVM. Also, I wonder if any of the MVVM toolkits would have facilities for this sort of thing already...
If you want to schedule some work from a background thread to the UI thread in WPF, use the DispatcherObject. Here's a nice article on how to Build More Responsive Apps with the Dispatcher.
Update: Note that if you use an event to send notifications from the Model to the ViewModel, you still need to switch to the UI thread somewhere. Whether that switch should be in the Model or the ViewModel is a good design discussion, but it's orthogonal to your question.
The event will be raised on the corresponding Dispatcher thread. Since you need to get to the UI thread, you need to use a Dispatcher that is created on the UI thread. The easiest way to get one is to use the DispatcherObject.Dispatcher property on one of the UI elements. The alternative is to create one in your Model or ViewModel. If you are a design purist, I would suggest you create the Dispatcher in your Model and dispatch the call back to the UI thread before you raise the event to which the ViewModel is listening. This way all the thread switching and management is contained in your Model and the ViewModel works as a single-threaded on the UI thread only.
I think that your ViewModel really shouldn't know anything about the View, including whether or not it's a WPF UI, or whether or not that UI even has the concept of a Dispatcher thread, so the red flag should fly as soon as you start writing code in your ViewModel that attempts to CheckAccess() or InvokeRequired in order to marshal some code to the UI thread. Instead I'd have the model raise an event that the View can listen for and update itself accordingly, or have the ViewModel expose a property (eg. bool FileIsLoading) that the View simply binds to in order to detect and display what the model is doing asynchronously, and it's the ViewModel's responsibility to ensure that the value of that property is accurate.
For example:
public partial class MainWindow : Window {
private ViewModel _model = new ViewModel();
public MainWindow() {
InitializeComponent();
DataContext = _model;
}
private void Button_Click(object sender, RoutedEventArgs e) {
_model.Run();
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Click="Button_Click"
Content="Run"
IsEnabled="{Binding IsIdle}" />
</Grid>
</Window>
public class ViewModel : INotifyPropertyChanged {
private bool _isIdle = true;
public bool IsIdle {
get { return _isIdle; }
set {
_isIdle = value;
OnPropertyChanged("IsIdle");
}
}
public void Run() {
ThreadPool.QueueUserWorkItem((state) => {
IsIdle = false;
Thread.Sleep(10000);
IsIdle = true;
});
}
#region INotifyPropertyChanged Implementation
protected void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null) {
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
I've got another approach that seems to work, and I just wanted to throw it out there to get some comments (if anyone is even reading this question anymore!).
I started to use the MVVM Light Toolkit's Messenger class, and it seems to work really well for me. For example, let's take the ProgressBar as an example. I registered two messages with my ViewModel for setting the progress value and progress maximum. Then in my model, as it sets up the tasks and overall process, it sends these messages. When the VM receives the messages, it just updates databound values, and my GUI updates automatically! It's super duper easy, but I was wondering what you all thought about this approach. Is anyone else doing this without incident?

What is the "proper" way in WPF MVVM to bind a frameworkElement event to a viewmodel handler?

So here is my dilemma, I want to handle view events on my view model, trouble is, in order to add an event handler, my view has to have a code behind file and therefore a class attribute has to be set. I'm 99% sure this is a bad idea, and to be perfectly honest, i'm not even sure how to do it (other than the obvious x:Class="" part) What is the proper way to do this in an MVVM application?
<ResourceDictionary>
<DataTemplate DataType="{x:Type vm:OutletViewModel}">
<Button Click="IHaveNoBinding">
</DataTemplate>
</ResourceDictionary>
Use commands:
<Button Command="{Binding ACommandOnYourViewModel}"/>
See this post of mine for a useful command implementation you can use in your view models.
Assuming you can't use commands, use attached command behaviors.
I use Attached Behaviors. Attached behaviors basically translate events into commands. Check out this link for an example:
http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx
Here is the code for a TextChangedBehavior.
public static class TextChangedBehavior
{
public static readonly DependencyProperty TextChangedCommandProperty =
DependencyProperty.RegisterAttached("TextChangedCommand",
typeof(ICommand),
typeof(TextChangedBehavior),
new PropertyMetadata(null, TextChangedCommandChanged));
public static ICommand GetTextChangedCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(TextChangedCommandProperty);
}
public static void SetTextChangedCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(TextChangedCommandProperty, value);
}
private static void TextChangedCommandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
TextBoxBase textBox = obj as TextBoxBase;
if (textBox != null)
{
textBox.TextChanged += new TextChangedEventHandler(HandleTextChanged);
}
}
private static void HandleTextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
ICommand command = GetTextChangedCommand(textBox);
command.Execute(textBox.Text);
}
}
}
XAML:
<TextBox behavior:TextChangedBehavior.TextChangedCommand="{Binding TextChangedCommand}" />
Generally I will not use the attached behavior pattern for simple things like this. As a consultant I find It complicates things for newer developers.
So how then do you handle control interaction when no commands are available. Get ready to pick your self up off of the floor :-) I will often times use the code behind for this. The event handler in the code behind handles the event, it gathers any data it needs from the event args and then forwards the request to the View Model. You do not lose much by doing this as most things that do not support ICommand cannot leverage the hide/show/enable/disable anyway.
There are some rules however. The code behind can only be used for control forwarding to the View Model. As long as you don't pass event arguments directly to the View Model I think it is fine to use events in this way. The fact of that matter is in large scale applications you cannot always get away from having code behinds. If you use them as they were intended i.e. page controls I see no harm in doing so.
Code behind isn't a bad thing at all. There are enough scenarios where you can't use WPF data binding (e.g. PasswordBox) and then you have to create a code behind file.
How you can use a PasswordBox without binding is shown in the ViewModel example of this project:
WPF Application Framework (WAF)
http://waf.codeplex.com

Categories