Developing a WPF application using MVVM, I have hit a point where I've got a "save" button on an editable datagrid which does behind the scenes processing but doesn't actually update anything in the UI, so the user has no way of knowing that the save has been successful.
I'm pretty new to WPF, and I presumed there would be a simple flash message control that one could use to notify the user of success and then faded away without them having to do anything. But it seems there's nothing in vanilla WPF that can do this, and there don't seem to be a lot of custom solutions either.
I don't want to use any kind of messagebox because it forces the user to take an action to dismiss the alert - I need something that breifly flashes a message without interfering with their workflow. I'm after something a bit like the JavaScript Toastr library -
http://codeseven.github.io/toastr/demo.html
can anyone either point me at an existing control for this, or where I might start at building one?
I think you don't need any third party controls. You always may create a custom control, paste it in a layout and bind layouts Visibility property to your view model. Another option is to use StatusBar to notify clients like in Word or VisualStudio. There is a just brief example:
Somewhere in xaml:
// ..
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem>
<Label Content="{Binding Message}"></Label>
</StatusBarItem>
</StatusBar>
// ..
Somewhere in your code (I like to use async/await with WPF):
// ..
statusBarViewModel.Message = "Processing the file..."; // assumed that you bind this view model to the view
await DoWork(); // do much work
await statusBarViewModel.ShowMessageAndHide("File saved"); // show final message and hide it after some time
// ..
And StatusBarViewModel:
public class StatusBarViewModel : INotifyPropertyChanged
{
private string message = string.Empty;
public string Message
{
get { return message; }
set
{
if (value == message) return;
message = value;
OnPropertyChanged();
}
}
public async Task ShowMessageAndHide(string message)
{
Message = message;
await Task.Delay(5000);
Message = string.Empty;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
The way I do simple notifications about the end of processing in WPF is by displaying a tooltip. You can open the tooltip from code:
https://stackoverflow.com/a/1911721/3391743
ToolTip tooltip = new ToolTip{ Content = "My Tooltip" };
NameTextBox.ToolTip = tooltip;
tooltip.IsOpen = true;
Then you can use a timer like here:
https://stackoverflow.com/a/1091753/3391743
As for IImplementPropertyChanged, there's a nice attribute [PropertyChanged] in Fody Property Changed assembly available via NuGet, which does all the plumbing for you.
Look here for reference on Fody: https://github.com/Fody/PropertyChanged
You could use the "NotifyIcon" from hardcodet to achieve a toast-like notification in WPF.
On a side note, in order for your UI to update, the binding engine will need to know when something has changed. In .NET, the INotifyPropertyChanged interface serves this purpose, and you'll need to implement that on the members you want to update in your UI when they are changed.
Here is a SO post which covers how to implement it.
There are a couple of parts to what you are asking:
To have code that is executed "behind the scenes", it sounds like you are asking about asynchronous programming. See async/await.
To have a custom visual that can interact with the view-model, you should take a look at Microsoft Prism's IInteractionRequest implementation.
Here is a good example of a codeproject post talking about how to do this with the Microsoft Prism framework, however you can adapt the code to work for you if you are not using that framework.
Related
I have a simple scenario - I have a "UserControl" down inside a WPF app. I want it to raise an event and catch that event at the main window, so I can call "show" to ensure the window is shown (e.g. not hidden in the tray).
I understand from here and here that a RoutedEvent is the way to go.
However, it is complicated because I am using ModernUI as a framework to set up the window. It looks great.
However, in MUI, I just populate "mui:LinkGroup.Links" and the rest of the construction of "pages" is handled for me, so I can't seem to figure out how to refer down the logical tree to actually set up a subscriber to the event. The tree is hidden away in however MUI sets everything up.
So - has anyone done this before? Is there a way I can register a handler for a RoutedEvent using MUI?
Or is there some other way of dealing with events propagated up the tree?
Thanks in advance!
Are you implementing MVVM? In that pattern, if based on a framework like Caliburn Micro or Prism you would use an EventAggregator to create a decoupled notification mechanism. If you are not using any of those frameworks you could search for a stand-alone version of an EventAggregator. But make sure that it uses weak references to keep track of the subscribers.
I would definitely prefer that approach toward bubbling events.
[EDIT]
For MVVMLight you would use the Messenger class. See Laurents article on MSDN Magazine.
It has a Send Method
Messenger.Default.Send(new AnyMessage());
and a Register method:
Messenger.Default.Register<AnyMessage>(
this,
message =>
{
// Do something
});
Just for completeness, and thanks to Marius, here's what I did:
Little message POCO:
class ConnectionStatusChanged
{
public bool NewStatus;
}
Code sending the message (from background thread in a view model):
Messenger.Default.Send(new ConnectionStatusChanged{NewStatus = '#YOURTRUEFALSEHERE#'});
Messenger receiver (with two lambda delegates, reads a bit ugly):
// Register an MVVM messenger handler to ensure we
// get any "connection state changed" messages so we can
// Maximise the window
Messenger.Default.Register<ConnectionStatusChanged>(
this,
(status) =>
{
// Note dispatcher helper from MVVM - if this occurs
// we need to use the helper to ensure the "event"
// fires on the main thread - a background thread trying
// to manipulate the window will throw an exception.
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
// Take window out of tray
Show();
// Put it on top
Activate();
});
});
Note I had to also use the DispatcherHelper as the code that sends the message is not on the main UI thread.
Works great!
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.
I have a ComboBox in a WPF app that has recently been refactored to use the MVVM pattern. An apparent side effect to this change is that changing focus to another application while the combobox dropdown is visible completely prevents the dropdown from being visible again, until the app has been restarted.
The ComboBox DataContext is set to my ViewModel, with its ItemsSource bound to an ObservableCollection<String> SearchSuggestions, and IsDropdownOpen bound to a property SuggestionsVisible in the ViewModel.
The desired effect is a search box with autocomplete suggestions. It should close if there are no suggestions in the ObservableCollection, if the user cancels the search, if the user runs the search, or if the user clicks away from the text field - either inside the app or outside it.
The ViewModel explicitly sets the SuggestionsVisible property to true or false based on whether SearchSuggesions contains any items after user input. This process continues to take place after this bug manifests itself, just with no visible change to the UI. Any idea why losing focus while the dropdown is open renders the dropdown un-openable for the rest of the app's session?
Here's how I have things wired together:
<ComboBox DataContext="{Binding SearchBoxVm}" Name="cmboSearchField" Height="0.667"
VerticalAlignment="Top" IsEditable="True" StaysOpenOnEdit="True"
PreviewKeyUp="cmboSearchField_OnKeyUp"
PreviewMouseLeftButtonUp="cmboSearchField_OnPreviewMouseLeftButtonUp"
Background="White" ItemsSource="{Binding SearchTopics}"
IsDropDownOpen="{Binding SuggestionsVisible,
UpdateSourceTrigger=PropertyChanged}"
Margin="50.997,15.333,120.44,0"
RenderTransformOrigin="0.5,0.5" Grid.Row="1" >
<!-- SNIP STYLING -->
</ComboBox>
ViewModel:
public class SearchBoxViewModel : INotifyPropertyChanged
{
public void ResetSearchField(bool preserveContents = false)
{
if (!preserveContents || string.IsNullOrEmpty(Query))
{
Foreground = Brushes.Gray;
QueryFont = FontStyles.Italic;
Query = DEFAULT_TEXT;
}
}
public bool OnKeyUp(Key key)
{
bool showDropdown = SuggestionsVisible;
bool changeFocusToCombobox = false;
if (keyInValidRange(key))
{
SearchSuggestions = GetSearchSuggestions(Query);
if (SearchSuggestions.Count > 0)
{
SuggestionsVisible = true;
}
}
return changeFocusToCombobox;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
bool _suggestionsVisible = false;
public bool SuggestionsVisible
{
get { return _suggestionsVisible; }
set
{
// this section is still called after this issue manifests,
// but no visible change to the UI state is made
_suggestionsVisible = value;
NotifyPropertyChanged("SuggestionsVisible");
}
}
public ObservableCollection<String> SearchTopics = new ObservableCollection<String>();
}
The OnKeyUp() method is called by the MainWindow class ( haven't gotten as far as binding events to handlers specified in the ViewModel ), while but there's also a call to ResetSearechField from the MainWindow:
// Note: removing references to this event handler does not have any effect
// on the issue at hand... only including here for completeness
void window_Deactivated(object sender, EventArgs e)
{
SearchBoxVm.SuggestionsVisible = false;
SearchBoxVm.ResetSearchField(true);
}
I've spent quite a bit of time trying to debug this, and haven't seen any internal state changes that might account for this. The NotifyPropertyChanged event is otherwise behaving as it did before, and the stack trace window isn't showing any exceptions having been encountered.
Setting the binding mode on the IsDropdownOpen property to 'TwoWay' in the XAML hasn't had any effect either. Lastly, wrapping the assignment to SuggestionsVisible in a Dispatcher call on the main thread has had no effect on the issue either.
Any assistance would be appreciated.
#BrMcMullin, since you have stated that:
The desired effect is a search box with autocomplete suggestions.
may I ask, why do you choose to use standard ComboBox instead of specialized AutoCompleteBox that is available in the WPF Toolkit - February 2010 Release and seems like was especially designed for your case?
You may have noticed that first link points to documentation for its Silverlight predecessor, but don't worry - WPF Toolkit library include fully functional official WPF port of AutoCompleteBox from Silverlight. There is more info about this "event": AutoCompleteBox: Now with 100% more WPF.
With that control your auto complete popup could looks as simple as:
or as complex as:
So, if you will not manage to solve your issue with ComboBox's popup visibility, feel free to give a try to AutoCompleteBox. With it you could even leverage dynamic sorting of your suggestions if needed (just use answer from #adabyron).
I am writing an application based on MVVM architecture. The application has a Wizard like workflow. In couple of pages (views) in my application, I need a button to be auto-clicked when a certain condition is satisfied. The views are tied together using the root Wizard view model which has a ClickNextBtn command that is tied to the Next button in the root Wizard view. So, I need something like in the root Wizard view:
<DataTrigger Binding="{Binding Path=CanAutoClickNext}" Value="True">
<Setter Property="ClickBtn" Value="true" />
</DataTrigger>
The property in the above sample is meaningless, but hopefully it helps convey what I am trying to do.
The CanAutoClickNext bool property is available off of the Wizard view model.
On one of the views where I need the Next button auto-clicked, I tried passing the WizardViewModel as an argument to its corresponding view model's constructor when it is first instantiated in the root wizard view model, and then calling the ClickNextBtn off of it in a method therein later when the view is actually loaded. But that did not work, not surprisingly.
I know how to programmatically click a wpf button, but getting it all tied together in the framework I have is proving to a big challenge. Any feedback is appreciated.
UPDATE:
I ended up rewriting the UI design pattern (still MVVM) so that now instead of having to having to move to a next page automatically, the state within a page changes and a different set of controls become active. Users are then prompted to click next.
Like the comment's on your question stated, this should be a concern of the ViewModel to invoke the Click Handler.
How you could go about implementing this is very similar to something like this Question's answer
Now in MVVM, you should have your Button's connected to an ICommand in the ViewModel(If your using MVVM Light toolkit, it will be RelayCommand/RelayCommand<T>).
Now assuming this ICommand variable in your VM is called NextButtonCommand,
what you can do is
public bool CanAutoClickNext {
get {
return _canAutoClickNext;
}
private set {
if (value == _canAutoClickNext)
return;
_canAutoClickNext = value;
RaisePropertyChanged(() => CanAutoClickNext);
if (_canAutoClickNext)
NextButtonCommand.Execute(null);
}
}
with this, when your property in the VM CanAutoClickNext gets set to "True", the Execute function of the ICommand is automatically invoked by the VM. This seperates all the logic handling to the VM and keeps the View dumb as what is recommended by MVVM when it comes to application / business logic.
Side Note
The property CanAutoClickNext seems a waste if it's not being bound to anything from the View. If this is the case, I'd recommend just getting rid of that property and invoke the ICommand.Execute(null) from the place where the logic holds fit than use a property with INPC just for this case.
I'll follow up from a different angle. Let's say you have any message bus ready (IEventAggregator, IMessenger, doesn't matter). I'll use the Caliburn.Micro's IEventAggregator along with the nomenclature 'cause that's what I'm most familiar with. Now you might have a very simple event:
public class MoveNext
{
}
Then your 'host' viewmodel of the wizard:
public class WizardHost : IHandle<MoveNext>
{
private readonly IEventAggregator messageBus
public WizardHost(IEventAggregator messageBus)
{
this.messageBus = messageBus;
this.messageBus.Subscribe(this);
}
/here you might have the 'real' command method, e.g:
public void GoToNextQuestion()
{
// do stuff
}
public void Handle(MoveNext message)
{
GoToNextQuestion();
}
}
public class WizardPage
{
private readonly IEventAggregator messageBus;
private bool shouldMoveToNext;
public WizardPage(IEventAggregator messageBus)
{
this.messageBus = messageBus;
}
public void DoStuff()
{
//at some point, you might want to switch the flag or do whatever you need/want to do and:
if(shouldMoveToNext)
messageBus.Publish(new MoveNext());
}
}
Now when you DoStuff() in your wizard page, you can publish the event and the 'host' page will react and flip the page.
That's of course all nice if you're using any MVVM framework that's out there. MVVM Light has the Messenger, Caliburn.Micro has - as you might have noticed - IEventAggregator.
I am making a windows phone 7 and trying to do it using MVVM. I would like to keep my view model as clean as possible but I am unsure on how to make a dialog box. I am using MVVM light and I know they have Messaging system or something but not really sure how to use it.
I would like to use Guide.BeginShowMessageBox as this seems to give more features than the standard dialog box.
How can I do this without breaking the MVVM pattern. AS when I load up the view I want to have a loaded trigger to be triggered and then check some conditions. If conditions are met show the Dialog.
// Vm
public RelayCommand MainPageLoaded
{
get
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
// breaks MVVM now as have view code in viewmodel. Need to take out somehow
Guide.BeginShowMessageBox("Test", "Test network", new List<string>() { "Yes", "No" }, 0, MessageBoxIcon.Warning, asyncResult =>
{
int? returned = Guide.EndShowMessageBox(asyncResult);
// if yes then work offline mode? Maybe another property in ViewModel will get set to say offline mode?
}, null);
}
return null;
}
set
{
// Not sure what to put here.
}
}
// View
<i:Interaction.Triggers>
<i:EventTrigger>
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding MainPageLoaded}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Edit
Another problem I am having is. I have a list that is bound to some data that is stored in this property
public ObservableCollection<ContactGroup> ContactGroups { get; set; }
then on tap I have a relaycommand that should be triggered
public ICommand GroupContactTapped
{
get
{
return new RelayCommand<GestureEventArgs>(e =>
{
var selectedTextBlock = e.OriginalSource as TextBlock;
MessageBox.Show(selectedTextBlock.Tag.ToString());
});
}
}
Yet I don't know how to find which object was "tapped" without casting the source to a textblock.
Assuming that you have one mainpage/view that hosts all the other views, like a mainwindow:
I send a message event from the viewmodels, and the dialog box is handled in the code behind of the main window. This is the only codebehind I have in my project so I find it acceptable that the rest of the project can be strictly MVVM, with this one exception.
I send the message with the following (converted from VB so it might need work):
object message = new DialogMessage("YourMessage", YourFunctionThatHandlesCallback) {
Button = MessageBoxButton.YesNo,
Caption = "Caption Goes Here"
};
Messenger.Default.Send(message);
I register for the dialog box with the following in the main page code behind:
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
''single initialization of messanger for catching message box
Messenger.[Default].Register(Of DialogMessage)(Me, Sub(msg)
Dim result = MessageBox.Show(msg.Content, msg.Caption, msg.Button, MessageBoxImage.Warning)
''Send callback
msg.ProcessCallback(result)
End Sub)
End Sub
End Class
I could not succesfully convert the C# lambda so I had to leave it in VB. Hope this helps
There is a MessageBoxService in the Cimbalino Phone Windows Toolkit!
You can use that in a MVVM architecture.
What it truly means to follow "the MVVM pattern" is a very subjective thing.
For instance, some people will say you shouldn't show/launch a messagebox (of any type) from the VM, while others will say this is fine.
As with any ambiguity, you'll need to balance adherence to a pattern, with what's most appropriate for a specific project, with what's appropriate for the people developing and maintaining the code base.
In terms of MvvmLight, the messaging system it uses is for communicating from a viewmodel to either another viewmodel or a view, not for displaying messages to the user.
If you are going to use Guide.BeginShowMessageBox, particularly from a viewmodel, beware that it is non-blocking. If you want it to behave like a "regular" MessageBox you'll need to use it with a ManualResetEvent so that it's not possible to continue to interact with the app while the messagebox is displayed.