I'm developing a winforms app with lots of different forms and user controls. Is there a recommended pattern that I could implement that notifies the user that there are unsaved changes on the current form/control when the form/control is exiting and also when the app is closing?
Memento is a way to encapsulate undoable changes.
You can then keep a log of your uncommitted memento instances.
But that's usually way to complex.
State is usually best.
Your application has two "change" states: Saved All Changes, Unsaved Changes.
Each State has a transition rule based on "change" and "save" methods.
The Saved All Changes implementation of "save" does nothing.
The Unsaved Changes implementation of "save" sets the state to "Saved All Changes".
The Saved All Changes implementation "change" sets the state to Unsaved Changes.
The Unsaved Changes implementation of "change" does nothing.
I'm using LLBL Gen pro for the ORM so that has some good entity tracking built into the objects.
I've kind of rolled my own that seems to work pretty well.
I created a new interface that my base User Controls and base Forms implement:
public interface IClosingNotification
{
/// <summary>
/// True if there is a dirty entity (or a dirty entity in the collection) present
/// </summary>
bool DirtyEntityPresent { get; }
/// <summary>
/// Register an entity to be watched for changes
/// </summary>
/// <param name="entity"></param>
void RegisterForClosingNotification(IEntity entity);
/// <summary>
/// Register a collection to be watched for changes
/// </summary>
/// <param name="collection"></param>
void RegisterForClosingNotification(IEntityCollection collection);
/// <summary>
/// Returns true if the form should close without any notification
/// </summary>
/// <returns></returns>
bool ShouldClose();
}
In my base control/form I have a collection of entities that I watch on each form, and I have a CloseForm() method in these classes that I use when a form is closing.
In my forms, whenever I create an object I can then register it for closing notification using:
RegisterForClosingNotification(MyCustomer);
It works well in our scenario.
Related
I am using caliburn micro. My problem is how to manage dialogs.
The biggest problem is that because when not using window your code doesnt stop and wait.
So i did something like this.
public void ShowDialog(IScreen dialogModel, Action<object> callback = null)
{
ActivateItem(dialogModel);
if (callback != null)
dialogModel.Deactivated += delegate { callback(dialogModel); };
}
This has lots of problem.For example in case i want to show dialog A and then at callback i want to show dialog B under certain cases there comes a problem.I have to write an extra function for DoSomething in order not to duplicate.And i loose all the other local variables..The problem is bigger when more levels are required..
showDialog(A, (cb)=>{
if(...) {
showDialog(B,(cb2)=>{
DoSomething();
});
}
else{
DoSomething();
}
});
Also because i wanted to show one dialog at a time i extended Collection.OneActive . But this had problem too. In deactivate event callback i couldnt close all if i wanted to! Because it keeps in memory the next reference after Deactivated is triggered and even if you clear it it comes again..
How about using a class to track state information as you move between dialogs, rather than nesting closures as shown in your original example?
I think you're on the right track, but it seems like you have two problems:
The amount of nesting that you're doing is not good for code clarity.
You need a better way to capture local variables and state information between dialogs.
To solve the first problem, I'd recommend breaking apart your logic into different methods. Every time a dialog is deactivated, you could have a method to handle the logic that should be executed afterward.
To solve the second problem, you might try creating a class that is responsible for storing the information that you want to pass between dialogs. An instance of this class could be passed in as an argument into each method that is to be executed upon dialog deactivation.
Here's how you could accomplish this:
Conductor Class
public class DialogTestsViewModel : Conductor<object>.Collection.OneActive
{
/// <summary>
/// Shows a dialog and executes its callback if necessary.
/// </summary>
/// <param name="dialogModel">The dialog view model to be shown.</param>
/// <param name="callback">The callback to be executed when dialog is closed.</param>
public void ShowDialog(IScreen dialogModel, Action callback = null)
{
// Show the dialog.
ActivateItem(dialogModel);
// If there is a callback, call it when dialog is closed / deactivated.
if (callback == null) return;
dialogModel.Deactivated += (sender, args) => callback();
}
/// <summary>
/// This method kicks off the dialog chain.
/// </summary>
public void ShowFirstDialog()
{
// Create a new context. This will hold state information
// as it is passed between dialogs.
var context = new TestDialogContext();
// Create the first dialog's view model.
var viewModel = new FirstDialogViewModel();
// Show the first dialog.
ShowDialog(viewModel, () => OnFirstDialogDeactivated(viewModel, context));
}
/// <summary>
/// Logic to be executed when the first dialog is closed.
/// </summary>
/// <param name="viewModel">The first dialog's view model.</param>
/// <param name="context">The state information.</param>
private void OnFirstDialogDeactivated(FirstDialogViewModel viewModel, TestDialogContext context)
{
// Check the view model here and store state information inside the context.
if (viewModel.SomethingIsChecked)
{
context.ShouldShowSecondDialog = true;
}
// Use information in the view model or the context to decide if we should show the next dialog.
// You could also make a decision about which dialog to show next here.
if (context.ShouldShowSecondDialog)
{
var secondDialog = new SecondDialogViewModel();
ShowDialog(secondDialog, () => OnSecondDialogDeactivated(context));
}
}
/// <summary>
/// Logic to be executed when the second dialog is closed.
/// </summary>
/// <param name="context">The state information.</param>
private void OnSecondDialogDeactivated(TestDialogContext context)
{
// Do more stuff.
}
}
Dialog Context Class
Here is where you would store the state information that needed to be passed between dialogs. I've only included one property here as an example, but you could put a lot of info here.
/// <summary>
/// State information to be passed between dialogs.
/// </summary>
public class TestDialogContext
{
public bool ShouldShowSecondDialog { get; set; }
}
What ways can the SampleConfirmationDialog be unit tested? The SampleConfirmationDialog would be exercised via acceptance tests, however how could we unit test it, seeing as MessageBox is not abstract and no matching interface?
public interface IConfirmationDialog
{
/// <summary>
/// Confirms the dialog with the user
/// </summary>
/// <returns>True if confirmed, false if not, null if cancelled</returns>
bool? Confirm();
}
/// <summary>
/// Implementation of a confirmation dialog
/// </summary>
public class SampleConfirmationDialog : IConfirmationDialog
{
/// <summary>
/// Confirms the dialog with the user
/// </summary>
/// <returns>True if confirmed, false if not, null if cancelled</returns>
public bool? Confirm()
{
return MessageBox.Show("do operation x?", "title", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes;
}
}
You can't, it's untestable in it's current state. For this particular class, there is also no value in unit testing it ... it is but a light wrapper around a built-in framework feature, so all you'd be doing is testing the framework.
If you absolutely must test it, the IConfirmationDialog interface should have another dependency that you can mock up in the unit test.
You should look into Typemock, a commercial mocking framework that lets you unit test these kinds of situations by using the .NET performance profiling libraries. See their website for more information.
I think it's OK to stop testing it at that level. Your interaction with IConfirmationDialog is more important than verifying that MessageBox.Show is actually getting called. Since that is an interface and is easily mockable, I think you are pretty well covered.
I plan to create a typical Master-Detail scenario, i.e. a collection of items displayed in a ListView via DataBinding to an ICollectionView, and details about the selected item in a separate group of controls (TextBoxes, NumUpDowns...).
No problem so far, actually I have already implemented a pretty similar scenario in an older project. However, it should be possible to select multiple items in the ListView and get the appropriate shared values displayed in the detail view. This means, if all selected items have the same value for a property, this value should be displayed in the detail view. If they do not share the same value, the corresponding control should provide some visual clue for the user indicating this, and no value should be displayed (or an "undefined" state in a CheckBox for example). Now, if the user edits the value, this change should be applied to all selected items.
Further requirements are:
MVVM compatibility (i.e. not too much code-behind)
Extendability (new properties/types can be added later on)
Does anyone have experience with such a scenario? Actually, I think this should be a very common scenario. However, I could not find any details on that topic anywhere.
Thanks!
gehho.
PS: In the older project mentioned above, I had a solution using a subclass of the ViewModel which handles the special case of multi-selection. It checked all selected items for equality and returned the appropriate values. However, this approach had some drawbacks and somehow seemed like a hack because (besides other smelly things) it was necessary to break the synchronization between the ListView and the detail view and handle it manually.
In your ViewModel, create a property that will bind to your ListView's SelectedItems.
Create another property that will represent the details object of your selected items.
The details section (in the XAML) is binding to this details property (in the ViewModel).
Every time selected items collection is modified (setter/CollectionChanged event), you need to update your details object as well (that will iterate through the relevant properties and decide if they're of the same value or not).
Once a property in the details object is modified, you need to iterate back on your selected items collection and make the relevant changes on them.
That's it.
Hope it helps
I am running into this same exact issue.
IsSynchronizedWithCurrentItem = "True" only keeps the CurrentItem in sync (the last item you selected without holding ctrl or shift).
Just as you probably have, I have searched the internet far and wide for an answer and never came up with one. My scenario has a 3 tier Master>Detail>Detail binding, where each tier is bound to it's own ListBox.
I've rigged up something that works for the time being.
For my Master>Detail>Detail tiers I created an individual CollectionViewSource for each tier and set that CollectionViewSource.Source to the appropriate entity object. On the MasterView bound ListBox's SelectionChanged Event I performed a filter on the MasterView.View to retrieve objects where the Master Primary Key = Detail Foreign Key.
It's sloppy, but if you have found a better way to get this done, I'd love to hear it.
I have used an approach similar to the one suggested by Captain. I have created one property in my ViewModel which represents multiple items (i.e. all selected items). In the property get- and set-accessors I have used the following methods to determine/set the shared values of/for all items. This approach does not use any reflection, but uses delegates in the form of lambda expressions which makes it pretty fast.
As an overview, this is my basic design:
public class MyMultiSelectionViewModel
{
private List<MyItemType> m_selectedItems = new List<MyItemType>();
public void UpdateSelectedItems(IList<MyItemType> selectedItems)
{
m_selectedItems = selectedItems;
this.OnPropertyChanged(() => this.MyProperty1);
this.OnPropertyChanged(() => this.MyProperty2);
// and so on for all relevant properties
}
// properties using SharedValueHelper (see example below)
}
The properties look like this:
public string Name
{
get
{
return SharedValueHelper.GetSharedValue<MyItemType, string>(m_selectedItems, (item) => item.Name, String.Empty);
}
set
{
SharedValueHelper.SetSharedValue<MyItemType, string>(m_selectedItems, (item, newValue) => item.Name = newValue, value);
this.OnPropertyChanged(() => this.Name);
}
}
And the code for the SharedValueHelper class looks like this:
/// <summary>
/// This static class provides some methods which can be used to
/// retrieve a <i>shared value</i> for a list of items. Shared value
/// means a value which represents a common property value for all
/// items. If all items have the same property value, this value is
/// the shared value. If they do not, a specified <i>non-shared value</i>
/// is used.
/// </summary>
public static class SharedValueHelper
{
#region Methods
#region GetSharedValue<TItem, TProperty>(IList<TItem> items, Func<TItem, TProperty> getPropertyDelegate, TProperty nonSharedValue)
/// <summary>
/// Gets a value for a certain property which represents a
/// <i>shared</i> value for all <typeparamref name="TItem"/>
/// instances in <paramref name="items"/>.<br/>
/// This means, if all wrapped <typeparamref name="TItem"/> instances
/// have the same value for the specific property, this value will
/// be returned. If the values differ, <paramref name="nonSharedValue"/>
/// will be returned.
/// </summary>
/// <typeparam name="TItem">The type of the items for which a shared
/// property value is requested.</typeparam>
/// <typeparam name="TProperty">The type of the property for which
/// a shared value is requested.</typeparam>
/// <param name="items">The collection of <typeparamref name="TItem"/>
/// instances for which a shared value is requested.</param>
/// <param name="getPropertyDelegate">A delegate which returns the
/// property value for the requested property. This is used, so that
/// reflection can be avoided for performance reasons. The easiest way
/// is to provide a lambda expression like this:<br/>
/// <code>(item) => item.MyProperty</code><br/>
/// This expression will simply return the value of the
/// <c>MyProperty</c> property of the passed item.</param>
/// <param name="nonSharedValue">The value which should be returned if
/// the values are not equal for all items.</param>
/// <returns>If all <typeparamref name="TItem"/> instances have
/// the same value for the specific property, this value will
/// be returned. If the values differ, <paramref name="nonSharedValue"/>
/// will be returned.</returns>
public static TProperty GetSharedValue<TItem, TProperty>(IList<TItem> items, Func<TItem, TProperty> getPropertyDelegate, TProperty nonSharedValue)
{
if (items == null || items.Count == 0)
return nonSharedValue;
TProperty sharedValue = getPropertyDelegate(items[0]);
for (int i = 1; i < items.Count; i++)
{
TItem currentItem = items[i];
TProperty currentValue = getPropertyDelegate(currentItem);
if (!sharedValue.Equals(currentValue))
return nonSharedValue;
}
return sharedValue;
}
#endregion
#region SetSharedValue<TItem, TProperty>(IList<TItem> a_items, Action<TItem, TProperty> a_setPropertyDelegate, TProperty a_newValue)
/// <summary>
/// Sets the same value for all <typeparamref name="TItem"/>
/// instances in <paramref name="a_items"/>.
/// </summary>
/// <typeparam name="TItem">The type of the items for which a shared
/// property value is requested.</typeparam>
/// <typeparam name="TProperty">The type of the property for which
/// a shared value is requested.</typeparam>
/// <param name="items">The collection of <typeparamref name="TItem"/>
/// instances for which a shared value should be set.</param>
/// <param name="setPropertyDelegate">A delegate which sets the
/// property value for the requested property. This is used, so that
/// reflection can be avoided for performance reasons. The easiest way
/// is to provide a lambda expression like this:<br/>
/// <code>(item, newValue) => item.MyProperty = newValue</code><br/>
/// This expression will simply set the value of the
/// <c>MyProperty</c> property of the passed item to <c>newValue</c>.</param>
/// <param name="newValue">The new value for the property.</param>
public static void SetSharedValue<TItem, TProperty>(IList<TItem> items, Action<TItem, TProperty> setPropertyDelegate, TProperty newValue)
{
if (items == null || items.Count == 0)
return;
foreach (TItem item in items)
{
try
{
setPropertyDelegate(item, newValue);
}
catch (Exception ex)
{
// log/error message here
}
}
}
#endregion
#endregion
}
I have searched on google about how to get started with the dependency property used in WPF/silverlight but didn't get any idea of the dependency property, can any one tell me about it , from beginner point of view, so that I get some idea about it and use it in my project
thanks in advance.
Can any one give me link or code example of simple application which explain in simple manner what is dependency Property is ???
I will be very thankfull
I find that implementing a DependencyProperty often involves four parts:
The DependencyProperty itself
Property with get and set
Static changed handler
Instance change handler
You can add a dependency property to a UserControl so that you can data bind to something in the DataContext where the UserControl is instantiated. For example you could add a property to a SoUserControl:
#region SampleProperty // Demo for SO 2424526
public static readonly DependencyProperty SamplePropertyProperty
= DependencyProperty.Register("SampleProperty", typeof(int), typeof(SoUserControl), new PropertyMetadata(OnSamplePropertyChanged));
/// <summary>
/// Demo for SO 2424526
/// Gets or sets dependency property.
/// </summary>
public int SampleProperty
{
get { return (int)GetValue(SamplePropertyProperty); }
set { SetValue(SamplePropertyProperty, value); }
}
/// <summary>
/// Handld changes to SamplePropertyProperty by calling a handler in the associated object.
/// </summary>
/// <param name="obj">object the property is associated with</param>
/// <param name="e">details of change</param>
static void OnSamplePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
(obj as SoUserControl).OnSamplePropertyChanged(e);
}
/// <summary>
/// Handle changes to the SamplePropertyProperty dependency property.
/// </summary>
/// <param name="e">details of change</param>
private void OnSamplePropertyChanged(DependencyPropertyChangedEventArgs e)
{
int SamplePropertyNewValue = (int)e.NewValue;
// do something with the internal logic of the control
}
#endregion
From my use with Dependency Properties. They become most important when binding. When you bind to display a regular property, the initial binding will work great, however, the UI will not always update when the property changes, in which case you could implement INotifyPropertyChanged on your class and raise a the NotifyPropertyChanged event, but a Dependency Property will update for you without implementing INotifyProperty Changed.
Triggers is another big one. If you wish to create a Trigger to fire off a WPF Animation whenever one of your properties is set to a certain value... then you need to be triggering off of a dependency property.
DepenedencyProperties can only be implemented on types that derive from DependencyObject.
UIElement derives from Visual which derives from DependencyObject, so most properties from .Net controls are dependency properties already. And when creating and registering my own DependencyProperties it's usually on my custom UserControls as that inherits from UIElement.
It was very frustrating for me when I first tried to create a dependency property on one of my regular classes (you're better off using INotifyPropertyChanged here)
Links:
http://msdn.microsoft.com/en-us/library/ms752914.aspx, http://www.wpftutorial.net/DependencyProperties.html
Have a look at Luis Abreu's blog :http://msmvps.com/blogs/luisabreu/
He's got a lot of info there about dependecy properties and how to use them.
I think you can compare it to the keyword yield.
it allows you to generate a lazy list, or an infinite generator.
every time it's needed one item is requested from that list, and the code continue with the flow.
A dependency property is the same idea for values or content. it computes the needed value, lazily, based on the other values it depends on.
it can cache the value, and when the underlying parts are "dirty", that is, something changed, it will propagate -- invalidate -- all the levels that depend on it.
the object needs to have the facilities of DependencyObject to participate (that is, so the engine will know about it, to introduce it to the chain.)
the mechanism is generic and unfixed, there isn't just one interface to comply with.
but it's somewhat more complex than what I said.
I have found a good explanation, to wire my understanding, in the following blog post:
http://blog.hackedbrain.com/2004/12/04/understanding-dependencyobject-and-dependencyproperty/
You should read a book, for starters.
I have read WPF In Action With Visual Sutdio 2005 from Manning.
As an answer for the negative punctuation just given to me, Dependency Property is part of a bigger plan called WPF and one can't understand Dependency Properties without the basics.
Therefore i think it's a waste of time to try to understand only what is a Dependency Property because i fear one will end up using it incorrectly.
Also, MSDN website has free information about WPF and Dependency Property.
I have a control that has its data bound to a standard ObservableCollection, and I have a background task that calls a service to get more data.
I want to, then, update my backing data behind my control, while displaying a "please wait" dialog, but when I add the new items to the collection, the UI thread locks up while it re-binds and updates my controls.
Can I get around this so that my animations and stuff keep running on my "please wait" dialog?
Or at least give the "appearance" to the user that its not locked up?
If i understand correctly, you already use a BackgroundWorker to retrieve the data, and that simply assigning this data to the ObservableCollection is locking up the UI.
One way to avoid locking up the UI is to assign the data to the ObservableCollection in smaller chunks by queuing multiple dispatcher methods. Between each method call, UI events can be handled.
the following would add one item on at a time, that's a bit extreme, but it illustrates the concept.
void UpdateItems()
{
//retrievedItems is the data you received from the service
foreach(object item in retrievedItems)
Dispatcher.BeginInvoke(DispatcherPriority.Background, new ParameterizedThreadStart(AddItem), item);
}
void AddItem(object item)
{
observableCollection.Add(item);
}
ObservableCollection will raise CollectionChanged events that will force UI to rebind data, measure, arrange and redraw. This might take a lot of time if you have many updates coming.
It is possible to make user think that UI is alive by splitting the job in small packages. Use Dispatcher from UI thread (any control has reference to it) to schedule collection update actions with 10-100 items (determine number by experiment, these just to support the idea).
Your background code might looks like this:
void WorkInBackground()
{
var results = new List<object>();
//get results...
// feed UI in packages no more than 100 items
while (results.Count > 0)
{
Application.Current.MainWindow.Dispatcher.BeginInvoke(
new Action<List<object>>(FeedUI),
DispatcherPriority.Background,
results.GetRange(0, Math.Min(results.Count, 100)));
results.RemoveRange(0, Math.Min(results.Count, 100));
}
}
void FeedUI(List<object> items)
{
// items.Count must be small enough to keep UI looks alive
foreach (var item in items)
{
MyCollection.Add(item);
}
}
I have a DLL which runs a worker thread and sends events back to the application - worked perfectly on windows forms, switched to WPF and everything stopped working. I've been smashing my head against a brick wall for 4 hours trying to get this to work. But the solution I ended up with, thanks to Microsoft's UI Thread Safe marshalling EnableCollectionSynchronization, gives a really clean implementation to solve this.
This Collection extends ObservableCollection and implements EnableCollectionSynchronization making these objects usable between WPF and also background workers.
Edit: Microsoft's docs say the following, so I'm going to assume that the object context sharing doesn't matter.
The context parameter is an arbitrary object that you can use to information known when you enable collection synchronization. Context can be null.
ThreadSafeCollection.cs
using System.Collections.ObjectModel;
using System.Windows.Data;
namespace NSYourApplication
{
/// <summary>
/// This ObservableCollection is thread safe
/// You can update it from any thread and the changes will be safely
/// marshalled to the UI Thread WPF bindings
/// Thanks Microsoft!
/// </summary>
/// <typeparam name="T">Whatever type of collection you want!</typeparam>
public class ThreadSafeCollection<T> : ObservableCollection<T>
{
private static object __threadsafelock = new object();
public ThreadSafeCollection()
{
BindingOperations.EnableCollectionSynchronization(this, __threadsafelock);
}
}
}
Example WindowViewModel
WindowViewModel.cs
namespace NSYourApplication
{
/// <summary>
/// Example View
/// BaseModelView implements "PropertyChanged" to update WPF automagically
/// </summary>
class TestViewModel : BaseModelView
{
public ThreadSafeCollection<string> StringCollection { get; set; }
/// <summary>
/// background thread implemented elsewhere...
/// but it calls this method eventually ;)
/// Depending on the complexity you might want to implement
/// [MethodImpl(MethodImplOptions.Synchronized)]
/// to Synchronize multiple threads to prevent chase-conditions,deadlocks etc
/// </summary>
public void NonUIThreadMethod()
{
// No dispatchers or invokes required here!
StringCollection.Add("Some Text from a background worker");
}
/// <summary>
/// Somewhere in the UIThread code it'll call this method
/// </summary>
public void UIThreadMethod()
{
StringCollection.Add("This text come from UI Thread");
}
/// <summary>
/// Constructor, creates a thread-safe collection
/// </summary>
public TestViewModel()
{
StringCollection = new ThreadSafeCollection<string>();
}
}
}
Usage in a listbox in a xaml window/control
MainWindow.xaml
<ListBox x:Name="wpfStringCollection" ItemsSource="{Binding StringCollection,Mode=OneWay}">
</ListBox>
use BackgroundWorker to accomplish this task. update the obsrvablecollection in the DoWork method
Use this:
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(UpdateData), value);
private void UpdateData(int value)
{
BindingSourceProperty = value;
}