YAY IT'S WORKING

This commit is contained in:
Antony Male 2015-09-24 10:34:19 +01:00
parent f4bf876c6b
commit 540482a5f4
6 changed files with 88 additions and 74 deletions

View File

@ -246,7 +246,7 @@ namespace Stylet
if (viewAsFrameworkElement != null)
{
logger.Info("Setting {0}'s DataContext to {1}", view, viewModel);
View.SetDataContext(viewAsFrameworkElement, viewModel);
viewAsFrameworkElement.DataContext = viewModel;
}
var viewModelAsViewAware = viewModel as IViewAware;

View File

@ -4,6 +4,7 @@ using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Windows;
using System.Windows.Data;
using System.Globalization;
namespace Stylet.Xaml
{
@ -69,13 +70,29 @@ namespace Stylet.Xaml
this.ActionNonExistentBehaviour = actionNonExistentBehaviour;
this.logger = logger;
var binding = new Binding()
var multiBinding = new MultiBinding();
multiBinding.Converter = new ActionTargetMultiValueConverter();
multiBinding.Bindings.Add(new Binding()
{
Path = new PropertyPath(View.ActionTargetProperty),
Mode = BindingMode.OneWay,
Source = this.Subject,
};
BindingOperations.SetBinding(this, targetProperty, binding);
});
multiBinding.Bindings.Add(new Binding()
{
Path = new PropertyPath(View.BackupActionTargetBindingProxyProperty),
Mode = BindingMode.OneWay,
Source = this.Subject,
Converter = new BindingProxyToValueConverter(),
});
//var binding = new Binding()
//{
// Path = new PropertyPath(View.ActionTargetProperty),
// Mode = BindingMode.OneWay,
// Source = this.Subject,
//};
BindingOperations.SetBinding(this, targetProperty, multiBinding);
}
private void UpdateActionTarget(object oldTarget, object newTarget)
@ -196,5 +213,24 @@ namespace Stylet.Xaml
ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
}
private class ActionTargetMultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (var value in values)
{
if (value != View.InitialActionTarget)
return value;
}
return View.InitialActionTarget;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
}
}

View File

@ -103,20 +103,26 @@ namespace Stylet.Xaml
// Seems this is the case when we're in a template. We'll get called again properly in a second.
// http://social.msdn.microsoft.com/Forums/vstudio/en-US/a9ead3d5-a4e4-4f9c-b507-b7a7d530c6a9/gaining-access-to-target-object-instead-of-shareddp-in-custom-markupextensions-providevalue-method?forum=wpf
if (!(valueService.TargetObject is DependencyObject))
var targetObjectAsDependencyObject = valueService.TargetObject as DependencyObject;
if (targetObjectAsDependencyObject == null)
return this;
// In some cases, the View.ActionTarget attached property won't be propagated - think popups, context menus, KeyBindings, etc
// In this case, we can grab a reference to the last-set View.ActionTarget using the dynamic resources mechanism
var resourceReference = new DynamicResourceExtension(View.ActionTargetProxyResourceKey).ProvideValue(serviceProvider);
targetObjectAsDependencyObject.SetValue(View.BackupActionTargetBindingProxyProperty, resourceReference);
var propertyAsDependencyProperty = valueService.TargetProperty as DependencyProperty;
if (propertyAsDependencyProperty != null && propertyAsDependencyProperty.PropertyType == typeof(ICommand))
{
// If they're in design mode and haven't set View.ActionTarget, default to looking sensible
return new CommandAction((DependencyObject)valueService.TargetObject, this.Method, this.CommandNullTargetBehaviour, this.CommandActionNotFoundBehaviour);
return new CommandAction(targetObjectAsDependencyObject, this.Method, this.CommandNullTargetBehaviour, this.CommandActionNotFoundBehaviour);
}
var propertyAsEventInfo = valueService.TargetProperty as EventInfo;
if (propertyAsEventInfo != null)
{
var ec = new EventAction((DependencyObject)valueService.TargetObject, propertyAsEventInfo.EventHandlerType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
var ec = new EventAction(targetObjectAsDependencyObject, propertyAsEventInfo.EventHandlerType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
return ec.GetDelegate();
}
@ -127,7 +133,7 @@ namespace Stylet.Xaml
var parameters = propertyAsMethodInfo.GetParameters();
if (parameters.Length == 2 && typeof(Delegate).IsAssignableFrom(parameters[1].ParameterType))
{
var ec = new EventAction((DependencyObject)valueService.TargetObject, parameters[1].ParameterType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
var ec = new EventAction(targetObjectAsDependencyObject, parameters[1].ParameterType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
return ec.GetDelegate();
}
}

View File

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace Stylet.Xaml
{
@ -23,4 +25,21 @@ namespace Stylet.Xaml
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new PropertyMetadata(null));
}
internal class BindingProxyToValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var proxy = value as BindingProxy;
if (proxy != null)
return proxy.Data;
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
}

View File

@ -19,8 +19,6 @@ namespace Stylet.Xaml
internal const string ActionTargetProxyResourceKey = "8b7cb732-8a14-4813-a580-b1f3cccea7b7";
internal const string DataContextProxyResourceKey = "982a3cb4-68b8-464f-9f65-8835d86d94dd";
/// <summary>
/// Initial value of the ActionTarget property.
/// This can be used as a marker - if the property has this value, it hasn't yet been assigned to anything else.
@ -34,23 +32,7 @@ namespace Stylet.Xaml
/// <returns>ActionTarget associated with the given object</returns>
public static object GetActionTarget(DependencyObject obj)
{
var actionTarget = obj.GetValue(ActionTargetProperty);
if (actionTarget == InitialActionTarget)
{
var frameworkElement = obj as FrameworkElement;
if (frameworkElement != null)
{
var bindingProxy = frameworkElement.TryFindResource(ActionTargetProxyResourceKey) as BindingProxy;
if (bindingProxy != null)
{
logger.Info("ActionTarget not set on object {0}, but a BindingProxy containing an ActionTarget was, so using that", obj);
actionTarget = bindingProxy.Data;
}
}
}
return actionTarget;
return obj.GetValue(ActionTargetProperty);
}
/// <summary>
@ -74,14 +56,22 @@ namespace Stylet.Xaml
if (frameworkElement == null)
return;
// Don't set if it's been set already
var currentValue = (BindingProxy)d.GetValue(BackupActionTargetBindingProxyProperty);
if (currentValue != null && currentValue.Data == e.NewValue)
return;
var bindingProxy = new BindingProxy()
{
Data = e.NewValue,
};
bindingProxy.Freeze();
frameworkElement.Resources[ActionTargetProxyResourceKey] = bindingProxy;
}));
internal static readonly DependencyProperty BackupActionTargetBindingProxyProperty =
DependencyProperty.RegisterAttached("BackupActionTargetBindingProxy", typeof(BindingProxy), typeof(View), new PropertyMetadata(null));
/// <summary>
/// Fetch the ViewModel currently associated with a given object
/// </summary>
@ -139,44 +129,6 @@ namespace Stylet.Xaml
}
}));
internal static void SetDataContext(FrameworkElement obj, object value)
{
obj.DataContext = value;
var bindingProxy = new BindingProxy()
{
Data = value,
};
obj.Resources[DataContextProxyResourceKey] = bindingProxy;
}
public static bool GetRestoreDataContext(DependencyObject obj)
{
return (bool)obj.GetValue(RestoreDataContextProperty);
}
public static void SetRestoreDataContext(DependencyObject obj, bool value)
{
obj.SetValue(RestoreDataContextProperty, value);
}
public static readonly DependencyProperty RestoreDataContextProperty =
DependencyProperty.RegisterAttached("RestoreDataContext", typeof(bool), typeof(View), new PropertyMetadata(false, (d, e) =>
{
if (!(e.NewValue is bool) || !(bool)e.NewValue)
return;
var frameworkElement = d as FrameworkElement;
if (frameworkElement == null)
return;
var bindingProxy = frameworkElement.Resources[DataContextProxyResourceKey] as BindingProxy;
if (bindingProxy == null)
return;
frameworkElement.DataContext = bindingProxy.Data;
}));
/// <summary>
/// Helper to set the Content property of a given object to a particular View
/// </summary>

View File

@ -6,13 +6,7 @@
<DockPanel LastChildFill="False">
<GroupBox DockPanel.Dock="Top" Header="ShowDialog and DialogResult" Padding="10">
<StackPanel Orientation="Horizontal">
<Button Command="{s:Action ShowDialogAndDialogResult}" Content="Show Dialog">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Foo" Command="{s:Action Foo}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button Command="{s:Action ShowDialogAndDialogResult}" Content="Show Dialog"/>
<TextBlock Margin="50,0,0,0">Result: </TextBlock>
<TextBlock Margin="10,0,0,0" Text="{Binding ShowDialogAndDialogResultDialogResult}"/>
</StackPanel>
@ -38,11 +32,18 @@
<GroupBox DockPanel.Dock="Top" Header="ActionTarget" Padding="10">
<DockPanel>
<TextBlock DockPanel.Dock="Top">Verify that pressing ctrl+s in the text box creates a dialog</TextBlock>
<TextBlock DockPanel.Dock="Top" TextWrapping="WrapWithOverflow">
Verify that pressing ctrl+s in the text box creates a dialog. Also verify that right-clicking in the text box and clicking the menu item shows a dialog.
</TextBlock>
<TextBox DockPanel.Dock="Top">
<TextBox.InputBindings>
<KeyBinding Key="S" Modifiers="Ctrl" Command="{s:Action ShowActionTargetSaved}"/>
</TextBox.InputBindings>
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Click Here" Command="{s:Action ShowActionTargetSaved}"/>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</DockPanel>
</GroupBox>