Catel and setting usercontrol's DataContext - wpf

I've tried to find a solution myself but I was not able, for some reason the DataContext is not correctly set up in the usercontrol's viewmodel
The idea is to have a single usercontrol that permits to perform a query on a fixed collection and allows the user to drop a treeviewitem that holds an item of the collection (in case the user have the treeview open)
In my main view I've defined :
<views:PortfolioChooserView x:Name="PortfolioChooserView" DataContext="{Binding PortfolioCompleteBox}" Height="25" LoadDefaultValue="True" />
Where PortfolioCompleteBox is a ViewModel defined in the MainViewModel as
public PortfolioChooserViewModel PortfolioCompleteBox
{
get { return GetValue<PortfolioChooserViewModel>(PortfolioChooserViewModelProperty); }
set { SetValue(PortfolioChooserViewModelProperty, value); }
}
public static readonly PropertyData PortfolioChooserViewModelProperty = RegisterProperty("PortfolioCompleteBox", typeof(PortfolioChooserViewModel));
public MainViewModel(ICreditLimitRepository creditLimitRepository, IDynamicContainer dynamicContainer)
{
this.creditLimitRepository = creditLimitRepository;
this.dynamicContainer = dynamicContainer;
LoadCreditLimitsCommand = new Command<object>(OnLoadCreditLimitsExecute, (() => OnLoadCreditLimitsCanExecute));
var viewModelFactory = this.GetServiceLocator().ResolveType<IViewModelFactory>();
PortfolioCompleteBox = viewModelFactory.CreateViewModel<PortfolioChooserViewModel>(null);
Model = new FiltersLoadModel();
}
My problem is that on the PortFolioChooserView I've the DataContext set to null (and I got 2 calls to the PortFolioChooserViewModel, one from the MainViewModel and the other one from the PortFolioChooserView's viewmodel locator)
public partial class PortfolioChooserView
{
private PortfolioChooserViewModel viewModel;
readonly bool isFirstLoad = true;
/// <summary>
/// Initializes a new instance of the <see cref="PortfolioChooserView"/> class.
/// </summary>
///
public PortfolioChooserView()
{
InitializeComponent();
if (isFirstLoad)
{
PortfolioCompleteBox.AllowDrop = true;
DragDropManager.AddPreviewDragOverHandler(PortfolioCompleteBox, OnElementDragOver);
DragDropManager.AddDropHandler(PortfolioCompleteBox, OnElementDrop);
isFirstLoad = false;
this.Loaded += PortfolioChooserView_Loaded;
this.DataContextChanged += PortfolioChooserView_DataContextChanged;
}
}
void PortfolioChooserView_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
int t = 0;
}
void PortfolioChooserView_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
viewModel = (PortfolioChooserViewModel)this.DataContext;
}
private void OnElementDragOver(object sender, Telerik.Windows.DragDrop.DragEventArgs e)
{
var options = Telerik.Windows.DragDrop.DragDropPayloadManager.GetDataFromObject(e.Data, TreeViewDragDropOptions.Key) as TreeViewDragDropOptions;
if (options != null)
{
var visual = options.DragVisual as TreeViewDragVisual;
if (visual != null) visual.IsDropPossible = true;
}
e.Handled = true;
}
private void OnElementDrop(object sender, Telerik.Windows.DragDrop.DragEventArgs e)
{
var context = ((IPortfolioAutoComplete)this.DataContext);
context.SetPortfolioAutoCompleteBox(e);
}
public static readonly DependencyProperty LoadDefaultValueProperty = DependencyProperty.Register(
"LoadDefaultValue", typeof(bool), typeof(PortfolioChooserView), new PropertyMetadata(default(bool)));
public bool LoadDefaultValue
{
get { return (bool)GetValue(LoadDefaultValueProperty); }
set { SetValue(LoadDefaultValueProperty, value); }
}
}
What am I doing wrong?
Thanks

Don't try to manage your own vm's
Catel will automatically accept a parent-vm as it's own vm as long as they are compatible. You don't need to handle this manually in your view loading in the view.
Instead of creating a VM in the parent VM, use a model only (so the vm only cares about what the VM itself should do). Then set the DC of the PortfolioChooserView to the model. Then the vm of the child view can accept the model in the ctor and be managed on it's own.
There are much better ways to communicate between vm's then trying to micro-manage like you are doing now. As always, see the docs.

Related

Dependency Property design data

I have a UserControl that has an ObservableCollection dependency property with a property changed callback. The callback rebuilds the nodes in a TreeView control. This all works fine but I would like to be able to have design data. Unfortunately, neither the constructor, the callback nor a default value function call is called by the designer unless I embed my control in another. Is there a way loading default data in this scenario?
Below is the code behind for my control
public partial class ScheduleResourcesSummaryTreeView : UserControl
{
public ObservableCollection<PerformanceProjection> SelectedPerformances
{
get { return (ObservableCollection<PerformanceProjection>)GetValue(SelectedPerformancesProperty); }
set { SetValue(SelectedPerformancesProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedPerformancesProperty =
DependencyProperty.Register("SelectedPerformances",
typeof(ObservableCollection<PerformanceProjection>),
typeof(ScheduleResourcesSummaryTreeView),
new FrameworkPropertyMetadata(
new ObservableCollection<PerformanceProjection>(),
FrameworkPropertyMetadataOptions.AffectsRender, SelectedPerformancesChanged)
);
private static void SelectedPerformancesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ScheduleResourcesSummaryTreeView resourcesTree)) return;
resourcesTree.ResourcesTree.Items.Clear();
var sessions = e.NewValue as ObservableCollection<PerformanceProjection> ?? new ObservableCollection<PerformanceProjection>();
if (sessions.Count == 0) return;
var projections = sessions.SelectMany(x => x.ResourceBookingProjections);
TreeNode<ResourceBookingProjection> resourceBookingTreeRoot = new RvtSummaryTree(new ObservableCollection<ResourceBookingProjection>(projections), "");
foreach (var treeNode in resourceBookingTreeRoot.Children)
{
resourcesTree.ResourcesTree.Items.Add((TreeNode<ResourceBookingProjection>)treeNode);
}
}
private static ObservableCollection<PerformanceProjection> DefaultCollection()
{
var prop = DesignerProperties.IsInDesignModeProperty;
var designMode = (bool) DependencyPropertyDescriptor
.FromProperty(prop, typeof(FrameworkElement))
.Metadata.DefaultValue;
if (!designMode)
return new ObservableCollection<PerformanceProjection>();
var designdata = new PerformanceProjectionsViewModelDesignData();
return designdata.SelectedPerformances;
}
public ScheduleResourcesSummaryTreeView()
{
InitializeComponent();
}
}

WPF: Handle a large number of routed events

I need your recomandations for the following problem:
Let say you have a MyView type (UserControl), which defines a routed event IsSelectedChanged. It is raised every time myView.IsSelected property value is changed.
Also, you have a MyContainer (Canvas), which contains a very (very!) large number of children of type MyView. MyContainer has routed event MyViewsSelectionChanged, that is raised whenever MyViewsSelection is changed. MyViewsSelection is a set of MyView objects that have IsSelected property set to true. MyContainer will handle MyView.IsSelectedChanged for every child and will provide its MyViewSelection status to the MyContainerParent (Panel)
MyContainerParent will handle myContainer.MyViewsSelectionChanged event
The issue I am afraid of is that my application will under-perform for a very large selection of MyView objects, resulting in a sort of 'wildfire' of events.
Any recomandations to prevent the issue, will be much appreciated!
Thanks
some code:
BatchView.IsSelectedChanged (MyView):
public static readonly RoutedEvent IsSelectedChangedEvent = EventManager.RegisterRoutedEvent(
"IsSelectedChanged",
RoutingStrategy.Direct,
typeof(RoutedEventHandler),
typeof(BatchView)
);
/// <summary>
/// Occurs when IsSelected property value is changed.
/// </summary>
public event RoutedEventHandler IsSelectedChanged {
add { AddHandler(IsSelectedChangedEvent, value); }
remove { RemoveHandler(IsSelectedChangedEvent, value); }
}
void RaiseIsSelectionChangedEvent() {
RoutedEventArgs e = new RoutedEventArgs(IsSelectedChangedEvent, this.BatchViewModel);
RaiseEvent(e);
Logger.Debug("IsSelectionChanged: {0}; IsSelected = {1}", this.BatchViewModel.Description, this.IsSelected);
}
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.RegisterAttached(
"IsSelected",
typeof(bool),
typeof(BatchView),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(delegate(DependencyObject sender, DependencyPropertyChangedEventArgs args) {
BatchView view = sender as BatchView;
bool isSelected = Convert.ToBoolean(args.NewValue);
if ( view != null ) {
view._border.BorderBrush = isSelected ? Brushes.Magenta : Brushes.Black;
view.IsPrimarySelected = view.IsFocused && isSelected;
}
})));
/// <summary>
/// Get/set whether this batch view is selected
/// </summary>
public bool IsSelected {
get { return (bool)GetValue(IsSelectedProperty); }
set {
if ( IsSelected != value ) {
SetValue(IsSelectedProperty, value);
RaiseIsSelectionChangedEvent();
}
}
}
GanttView (MyContainer):
static GanttView() {
EventManager.RegisterClassHandler(typeof(BatchView), BatchView.IsSelectedChangedEvent, new RoutedEventHandler(delegate(object sender, RoutedEventArgs args) {
var batchView = sender as BatchView;
var ganttView = batchView.FindVisualParent<GanttView>();
if ( ganttView != null ) {
ganttView.RaiseBatchViewsSelectionChangedEvent();
}
args.Handled = true;
}));
}
public static readonly RoutedEvent BatchViewsSelectionChangedEvent = EventManager.RegisterRoutedEvent(
"BatchViewsSelectionChanged",
RoutingStrategy.Direct,
typeof(RoutedEventHandler),
typeof(GanttView)
);
public event RoutedEventHandler BatchViewsSelectionChanged {
add { AddHandler(BatchViewsSelectionChangedEvent, value); }
remove { RemoveHandler(BatchViewsSelectionChangedEvent, value); }
}
void RaiseBatchViewsSelectionChangedEvent() {
RoutedEventArgs e = new RoutedEventArgs(BatchViewsSelectionChangedEvent, this);
RaiseEvent(e);
Logger.Debug("BatchViewsSelectionChanged: {0};", this.SelectedBatchViews.Count());
}
SchedulerView (MyContainerParent):
static SchedulerView() {
EventManager.RegisterClassHandler(typeof(GanttView), GanttView.BatchViewsSelectionChangedEvent, new RoutedEventHandler(delegate(object sender, RoutedEventArgs args) {
var schedulerView = ((GanttView)sender).FindVisualParent<SchedulerView>();
if ( schedulerView != null ) {
if ( schedulerView.BatchesSelectionChanged != null ) {
BatchesSelectionChangedEventArgs e = new BatchesSelectionChangedEventArgs();
e.SelectedBatchesCount = schedulerView.GanttView.SelectedBatchViews.Count();
e.TotalBatchesDuration = schedulerView.GanttView.SelectedBatchViews.Sum<BatchView>(bv => bv.BatchViewModel.Model.Duration);
e.TotalBatchesQuantity = schedulerView.GanttView.SelectedBatchViews.Sum<BatchView>(bv => bv.BatchViewModel.Model.Quantity);
schedulerView.BatchesSelectionChanged(schedulerView, e);
}
}
}));
}
If you are concerned about the number of events you have to process. You should re-evaluate your approach. Is there a way to determine when the user has finished selecting the items?
If there is no way of reducing the number of events then you might want to implement a throttle, i.e. you only process an event if no event has been received for a certain amount of time.
You can implement this yourself - e.g. by using a timer - or you can use the reactive extensions' (RX) Throttle function.
Throttle "Ignores the values from an observable sequence which are
followed by another value before due time with the specified source
and dueTime"
You can find the RX at http://msdn.microsoft.com/en-us/data/gg577609.aspx and the documentation for Trottle at http://msdn.microsoft.com/en-us/library/hh229298%28v=vs.103%29. Obviously you can also install RX via NuGet.
I want to share the solution to my problem. Solution is based on recomandation given by my manager and Obalix. So, I will use a timer, to delay a bit the raising of GanttView.BacthViewsSelectionChangedEvent.
static GanttView() {
EventManager.RegisterClassHandler(typeof(BatchView), BatchView.IsSelectedChangedEvent, new RoutedEventHandler(delegate(object sender, RoutedEventArgs args) {
var batchView = sender as BatchView;
var ganttView = batchView.FindVisualParent<GanttView>();
if ( ganttView != null && !ganttView._batchViewIsSelectedChangedEventQueued ) {
ganttView._batchViewIsSelectedChangedEventQueued = true;
System.Timers.Timer eventTrigger = new System.Timers.Timer(100) { AutoReset = false };
eventTrigger.Start();
eventTrigger.Elapsed += new System.Timers.ElapsedEventHandler(delegate(object timer, System.Timers.ElapsedEventArgs e) {
ganttView._batchViewIsSelectedChangedEventQueued = false;
ganttView.Dispatcher.Invoke(new Action(delegate() { ganttView.RaiseBatchViewsSelectionChangedEvent(); }), DispatcherPriority.Normal, null);
});
}
args.Handled = true;
}));
}
It is important to use the Dispatcher property for invokation of ganttView.RaiseBatchViewsSelectionChangedEvent(), otherwise you will get an exception (" The calling thread cannot access this object because a different thread owns it."), please refer to this post http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher
Dan and Obalix, thank you a lot for your time and considerations!

ObservableCollection has Depedency Property in Silverlight

Recently i was developing a custom control in Silverlight, I created custom dependency property which is of type ObservableCollection. I have another 2 custom dependency properties of type strings. My requirement is on addition of any item to collection, I have to fire collectionChanged Event , in this event handler, i want to update the other 2 dependency properties.
public static readonly DependencyProperty itemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<ValidationErrorMessage>), typeof(SummaryUserControl), new PropertyMetadata(new ObservableCollection<ValidationErrorMessage>(), new PropertyChangedCallback(fun1)));
public ObservableCollection<ValidationErrorMessage> Items
{
get
{
return (ObservableCollection<ValidationErrorMessage>)base.GetValue(itemsProperty);
}
set
{
base.SetValue(itemsProperty, value);
}
}
public static void fun1(object sender, DependencyPropertyChangedEventArgs evt)
{
var newValue = evt.NewValue as ObservableCollection<ValidationErrorMessage>;
if(newValue!=null)
newValue.CollectionChanged += new NotifyCollectionChangedEventHandler(CollectionChangedHandler);
var oldValue = evt.OldValue as ObservableCollection<ValidationErrorMessage>;
if(oldValue!=null)
oldValue.CollectionChanged -= new NotifyCollectionChangedEventHandler(CollectionChangedHandler);
}
static void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
var newItems = e.NewItems as ObservableCollection<ValidationErrorMessage>;
foreach (var item in newItems)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
static void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
public static readonly DependencyProperty headerProperty = DependencyProperty.Register("Header", typeof(String), typeof(SummaryUserControl), new PropertyMetadata(String.Empty, null));
public String Header
{
get
{
return (String)base.GetValue(headerProperty);
}
set
{
base.SetValue(headerProperty, value);
RaisePropertyChange("Header");
}
}
public static readonly DependencyProperty messageTypeProperty =
DependencyProperty.Register("MessageType", typeof(MessageEnumType), typeof(SummaryUserControl), new PropertyMetadata(MessageEnumType.Error, null));
public MessageEnumType MessageType
{
get { return (MessageEnumType)GetValue(messageTypeProperty); }
set { SetValue(messageTypeProperty, value); RaisePropertyChange("MessageType"); }
}
How can I change the values of the dependency properties messageType and Header? I'm unable to access those properties in either the CollectionChanged or NotifyPropertyChanged event since all those events are static. I cannot access the instance within these static event handlers.
I tried to fix the problem with a converter, but my curosity on Silverlight makes me want to use the above approach. How can I set values for those dependency properties within CollectionChanged event or NotifyPropertyChanged events?
The sender in your static fun1 method should be the instance of the class which declares the itemsProperty DependencyProperty. Therefore you can access the concrete instance with casting the sender to your class.
public static void fun1(object sender, DependencyPropertyChangedEventArgs evt)
{
MyClass concreteInstance = sender as MyClass;
if(concreateInstance != null)
{
[...your code...]
}
}

AttachedProperty in XAML firing before I can authenticate & determine role

Using VS2008, Silverlight 4.
I have created an AttachedProperty, "RequiresRole", that I'm setting in my XAML. If a user isn't in the role defined by that property, the visibility of said control is set to collapsed.
The problem I'm currently having is that the 'UserHasRole' check (within my Authorization class, as part of the RequiredRole_Callback function) is firing when I load the application, but before I have a chance to login, authenticate, and set the Role for the user. So the property is working, because the menu item is not visible, but it's not currently alerting my UI, after authentication, that the role has changed. I know where to implement it, just not how. INotifyPropertyChanged? Not sure how that'd fit.
I have a few properties in my App.xaml.cs, "IsAuthenticated", which is set to true when a user logs in (using a service to call a SPROC), and "Role", which is the return value of a successful call to Logon SPROC.
Relevant Authorization.cs code:
public static readonly DependencyProperty RequiredRoleProperty = DependencyProperty.RegisterAttached(
"RequiredRole",
typeof(string),
typeof(Authorization),
new PropertyMetadata(RequiredRole_Callback));
// This callback will be invoked when some control will receive a value for your 'RequiredRole' property
private static void RequiredRole_Callback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var uiElement = (UIElement)source;
RecalculateControlVisibility(uiElement);
// also this class should subscribe somehow to role changes and update all control's visibility after role being changed
}
private static void RecalculateControlVisibility(UIElement control)
{
//Authorization.UserHasRole() - is your code to check roles
if (Authorization.UserHasRole(GetRequiredRole(control)))
control.Visibility = Visibility.Visible;
else
control.Visibility = Visibility.Collapsed;
}
private static Boolean UserHasRole(string role)
{
string Role = App.Role;
if (Role.ToLower() == role.ToLower())
{
return true;
}
return false;
}
Relevant MainPage.xaml code:
xmlns:s="clr-namespace:TSMVVM.Authorization"
<HyperlinkButton x:Name="lnkLogin"
HorizontalAlignment="Right"
Tag="Login"
Command="{Binding NavigateCommand}"
s:Authorization.RequiredRole="Azdmin"
CommandParameter="{Binding Tag, ElementName=lnkLogin}"
Content="Login" />
Relevant Mainpage.xaml.cs code:
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
if (!App.IsAuthenticated)
{
ContentFrame.Navigate(new Uri("Login", UriKind.Relative));
}
}
Updated Code
(Note, changed RequiredRole to RequiresRole)
Authorization.cs
public class Authorization
{
#region Attached DP registration
public static string GetRequiresRole(UIElement obj)
{
return (string)obj.GetValue(RequiresRoleProperty);
}
public static void SetRequiresRole(UIElement obj, string value)
{
obj.SetValue(RequiresRoleProperty, value);
}
#endregion
/// Using a DependencyProperty as the backing store for requiresRole. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RequiresRoleProperty = DependencyProperty.RegisterAttached(
"RequiresRole",
typeof(string),
typeof(Authorization),
new PropertyMetadata(RequiresRole_Callback));
// This callback will be invoked when some control will receive a value for your 'RequiredRole' property
private static void RequiresRole_Callback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var uiElement = (UIElement)source;
if (App.IsAuthenticated)
{
RecalculateControlVisibility(uiElement);
}
else
{
EventHandler eh = null;
eh = delegate
{
RecalculateControlVisibility(uiElement);
((App)Application.Current).Authenticated -= eh;
};
((App)Application.Current).Authenticated += eh;
}
}
private static void RecalculateControlVisibility(UIElement control)
{
//Authorization.UserHasRole() - is your code to check roles
if (Authorization.UserHasRole(GetRequiresRole(control)))
control.Visibility = Visibility.Visible;
else
control.Visibility = Visibility.Collapsed;
}
private static Boolean UserHasRole(string role)
{
if (App.Role != null)
{
string Role = App.Role[0].ToString();
if (Role.ToLower() == role.ToLower())
{
return true;
}
}
return false;
}
}
App.xaml.cs
private static string _role;
public event EventHandler Authenticated = delegate { };
public static string Role
{
get
{
return _role;
}
set
{
_role = value;
}
}
MainPage.xaml:
<HyperlinkButton x:Name="lnkSiteParameterDefinitions"
Style="{StaticResource LinkStyle}"
Tag="SiteParameterDefinitions"
s:Authorization.RequiresRole="Admin"
Content="Site Parameter Definitions"
Command="{Binding NavigateCommand}"
CommandParameter="{Binding Tag, ElementName=lnkSiteParameterDefinitions}"/>
And finally, login.xaml.cs
private void LogonService_LogonCompleted(object sender, TSMVVMLogonSVC.LogonCompletedEventArgs e)
{
ObservableCollection<string> Result = e.Result;
switch (Result[0])
{
case "Logon Failed":
break;
case "Logon Successful":
PageConductor = new Services.PageConductor();
App.CurrentUser = Result[2];
App.Role = Result[1].ToString();
App.IsAuthenticated = true;
Messenger.Default.Send(new TSMVVM.Messages.FrameMessage() { RootFrame = (Frame)this.Parent});
PageConductor.GoToView("Home", "main");
break;
case "Change Password":
break;
}
}
I don't think INotifyPropertyChanged is really what you need in this case. You just need a one shot event when the application has authenticated.
Add an simple event to your App class:-
public event EventHandler Authenticated = delegate { };
Now modify your call back:-
private static void RequiredRole_Callback(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var uiElement = (UIElement)source;
if (App.IsAuthenticated)
{
RecalculateControlVisibility(uiElement);
}
else
{
EventHandler eh = null;
eh = delegate
{
RecalculateControlVisibility(uiElement);
((App)Application.Current).Authenticated -= eh;
};
((App)Application.Current).Authenticated += eh;
}
}
Note the removal of the event handler after it has fired is very important, without it you may well have a memory leak.

unit test an attached behavior wpf

I am still grokking attached behaviors in general, and am at a loss to see how to write a unit test for one.
I pasted some code below from Sacha Barber's Cinch framework that allows a window to be closed via attached behavior. Can somewone show me an example unit test for it?
Thanks!
Berryl
#region Close
/// <summary>Dependency property which holds the ICommand for the Close event</summary>
public static readonly DependencyProperty CloseProperty =
DependencyProperty.RegisterAttached("Close",
typeof(ICommand), typeof(Lifetime),
new UIPropertyMetadata(null, OnCloseEventInfoChanged));
/// <summary>Attached Property getter to retrieve the CloseProperty ICommand</summary>
public static ICommand GetClose(DependencyObject source)
{
return (ICommand)source.GetValue(CloseProperty);
}
/// <summary>Attached Property setter to change the CloseProperty ICommand</summary>
public static void SetClose(DependencyObject source, ICommand command)
{
source.SetValue(CloseProperty, command);
}
/// <summary>This is the property changed handler for the Close property.</summary>
private static void OnCloseEventInfoChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var win = sender as Window;
if (win == null) return;
win.Closing -= OnWindowClosing;
win.Closed -= OnWindowClosed;
if (e.NewValue == null) return;
win.Closing += OnWindowClosing;
win.Closed += OnWindowClosed;
}
/// <summary>
/// This method is invoked when the Window.Closing event is raised.
/// It checks with the ICommand.CanExecute handler
/// and cancels the event if the handler returns false.
/// </summary>
private static void OnWindowClosing(object sender, CancelEventArgs e)
{
var dpo = (DependencyObject)sender;
var ic = GetClose(dpo);
if (ic == null) return;
e.Cancel = !ic.CanExecute(GetCommandParameter(dpo));
}
/// <summary>
/// This method is invoked when the Window.Closed event is raised.
/// It executes the ICommand.Execute handler.
/// </summary>
static void OnWindowClosed(object sender, EventArgs e)
{
var dpo = (DependencyObject)sender;
var ic = GetClose(dpo);
if (ic == null) return;
ic.Execute(GetCommandParameter(dpo));
}
#endregion
You would likely use a lambda in your ICommand using a DelegateCommand or a RelayCommand. Multiple implementations of these exists all over the place and Cinch may have something similar. Really simple version (as an example, not meant for production use):
public class DelegateCommand : ICommand {
private Action _execute = null;
public void Execute( object parameter ) {
_execute();
}
public DelegateCommand( Action execute ) {
_execute = execute;
}
#region stuff that doesn't affect functionality
public bool CanExecute( object parameter ) {
return true;
}
public event EventHandler CanExecuteChanged {
add { }
remove { }
}
#endregion
}
Then your test body might look something like this:
bool wascalled = false;
var execute = new DelegateCommand(
() => {
wascalled = true;
} );
var window = new Window();
SomeClass.SetClose( window, execute );
// does the window need to be shown for Close() to work? Nope.
window.Close();
AssertIsTrue( wascalled );
This is an over-simplified example. There are of course other tests you'll want to perform, in which case you should create or find a fuller implementation of DelegateCommand that also properly implements CanExecute, among other things.
DependencyProperty changing and value coercion on their own looks like 'Impossible Dependencies' for me. Having reference to Window there makes things even trickier. I think I'd go with Humble Object pattern here...

Resources