mirror of https://github.com/AMT-Cheif/Stylet.git
Refactor to add {s:ViewModel}, and use that for actions
This commit is contained in:
parent
540482a5f4
commit
4d767f0364
|
@ -131,6 +131,7 @@
|
||||||
<Compile Include="Xaml\View.cs" />
|
<Compile Include="Xaml\View.cs" />
|
||||||
<Compile Include="ViewManager.cs" />
|
<Compile Include="ViewManager.cs" />
|
||||||
<Compile Include="WindowManager.cs" />
|
<Compile Include="WindowManager.cs" />
|
||||||
|
<Compile Include="Xaml\ViewModelExtension.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Include="MessageBoxView.xaml">
|
<Page Include="MessageBoxView.xaml">
|
||||||
|
|
|
@ -245,8 +245,9 @@ namespace Stylet
|
||||||
var viewAsFrameworkElement = view as FrameworkElement;
|
var viewAsFrameworkElement = view as FrameworkElement;
|
||||||
if (viewAsFrameworkElement != null)
|
if (viewAsFrameworkElement != null)
|
||||||
{
|
{
|
||||||
logger.Info("Setting {0}'s DataContext to {1}", view, viewModel);
|
logger.Info("Setting {0}'s DataContext and ViewModel proxy to {1}", view, viewModel);
|
||||||
viewAsFrameworkElement.DataContext = viewModel;
|
viewAsFrameworkElement.DataContext = viewModel;
|
||||||
|
View.SetViewModel(viewAsFrameworkElement, viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
var viewModelAsViewAware = viewModel as IViewAware;
|
var viewModelAsViewAware = viewModel as IViewAware;
|
||||||
|
|
|
@ -78,13 +78,7 @@ namespace Stylet.Xaml
|
||||||
Mode = BindingMode.OneWay,
|
Mode = BindingMode.OneWay,
|
||||||
Source = this.Subject,
|
Source = this.Subject,
|
||||||
});
|
});
|
||||||
multiBinding.Bindings.Add(new Binding()
|
multiBinding.Bindings.Add(View.GetBindingToViewModel(this.Subject));
|
||||||
{
|
|
||||||
Path = new PropertyPath(View.BackupActionTargetBindingProxyProperty),
|
|
||||||
Mode = BindingMode.OneWay,
|
|
||||||
Source = this.Subject,
|
|
||||||
Converter = new BindingProxyToValueConverter(),
|
|
||||||
});
|
|
||||||
|
|
||||||
//var binding = new Binding()
|
//var binding = new Binding()
|
||||||
//{
|
//{
|
||||||
|
|
|
@ -107,11 +107,6 @@ namespace Stylet.Xaml
|
||||||
if (targetObjectAsDependencyObject == null)
|
if (targetObjectAsDependencyObject == null)
|
||||||
return this;
|
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;
|
var propertyAsDependencyProperty = valueService.TargetProperty as DependencyProperty;
|
||||||
if (propertyAsDependencyProperty != null && propertyAsDependencyProperty.PropertyType == typeof(ICommand))
|
if (propertyAsDependencyProperty != null && propertyAsDependencyProperty.PropertyType == typeof(ICommand))
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,6 +28,8 @@ namespace Stylet.Xaml
|
||||||
|
|
||||||
internal class BindingProxyToValueConverter : IValueConverter
|
internal class BindingProxyToValueConverter : IValueConverter
|
||||||
{
|
{
|
||||||
|
public static readonly BindingProxyToValueConverter Instance = new BindingProxyToValueConverter();
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
var proxy = value as BindingProxy;
|
var proxy = value as BindingProxy;
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Stylet.Xaml
|
||||||
|
|
||||||
internal const string ViewManagerResourceKey = "b9a38199-8cb3-4103-8526-c6cfcd089df7";
|
internal const string ViewManagerResourceKey = "b9a38199-8cb3-4103-8526-c6cfcd089df7";
|
||||||
|
|
||||||
internal const string ActionTargetProxyResourceKey = "8b7cb732-8a14-4813-a580-b1f3cccea7b7";
|
internal const string ViewModelProxyResourceKey = "8b7cb732-8a14-4813-a580-b1f3cccea7b7";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initial value of the ActionTarget property.
|
/// Initial value of the ActionTarget property.
|
||||||
|
@ -49,28 +49,7 @@ namespace Stylet.Xaml
|
||||||
/// The object's ActionTarget. This is used to determine what object to call Actions on by the ActionExtension markup extension.
|
/// The object's ActionTarget. This is used to determine what object to call Actions on by the ActionExtension markup extension.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly DependencyProperty ActionTargetProperty =
|
public static readonly DependencyProperty ActionTargetProperty =
|
||||||
DependencyProperty.RegisterAttached("ActionTarget", typeof(object), typeof(View), new FrameworkPropertyMetadata(InitialActionTarget, FrameworkPropertyMetadataOptions.Inherits, (d, e) =>
|
DependencyProperty.RegisterAttached("ActionTarget", typeof(object), typeof(View), new FrameworkPropertyMetadata(InitialActionTarget));
|
||||||
{
|
|
||||||
// Also set a binding proxy if we can, in case there's something weird in the way
|
|
||||||
var frameworkElement = d as FrameworkElement;
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
frameworkElement.Resources[ActionTargetProxyResourceKey] = bindingProxy;
|
|
||||||
}));
|
|
||||||
|
|
||||||
internal static readonly DependencyProperty BackupActionTargetBindingProxyProperty =
|
|
||||||
DependencyProperty.RegisterAttached("BackupActionTargetBindingProxy", typeof(BindingProxy), typeof(View), new PropertyMetadata(null));
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetch the ViewModel currently associated with a given object
|
/// Fetch the ViewModel currently associated with a given object
|
||||||
|
@ -129,6 +108,47 @@ namespace Stylet.Xaml
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the ViewModel which can be subsequently retrieved using {s:ViewModel}
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="view">View to store the ViewModel for</param>
|
||||||
|
/// <param name="viewModel">ViewModel to store</param>
|
||||||
|
public static void SetViewModel(FrameworkElement view, object viewModel)
|
||||||
|
{
|
||||||
|
var bindingProxy = new BindingProxy()
|
||||||
|
{
|
||||||
|
Data = viewModel,
|
||||||
|
};
|
||||||
|
view.Resources[ViewModelProxyResourceKey] = bindingProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetch a binding which can be used to retrieve the ViewModel associated with a View
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="view">View to fetch the ViewModel for</param>
|
||||||
|
/// <returns>Binding which can retrieve the ViewModel</returns>
|
||||||
|
public static Binding GetBindingToViewModel(DependencyObject view)
|
||||||
|
{
|
||||||
|
if (view.GetValue(ViewModelProxyProperty) == null)
|
||||||
|
{
|
||||||
|
var resource = new DynamicResourceExtension(ViewModelProxyResourceKey).ProvideValue(null);
|
||||||
|
view.SetValue(ViewModelProxyProperty, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
var binding = new Binding()
|
||||||
|
{
|
||||||
|
Source = view,
|
||||||
|
Path = new PropertyPath(View.ViewModelProxyProperty),
|
||||||
|
Mode = BindingMode.OneWay,
|
||||||
|
Converter = BindingProxyToValueConverter.Instance,
|
||||||
|
};
|
||||||
|
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static readonly DependencyProperty ViewModelProxyProperty =
|
||||||
|
DependencyProperty.RegisterAttached("ViewModelProxy", typeof(BindingProxy), typeof(View), new PropertyMetadata(null));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to set the Content property of a given object to a particular View
|
/// Helper to set the Content property of a given object to a particular View
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Markup;
|
||||||
|
|
||||||
|
namespace Stylet.Xaml
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// MarkupExtension which can retrieve the ViewModel for the current View, if available
|
||||||
|
/// </summary>
|
||||||
|
public class ViewModelExtension : MarkupExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Instantiates a new instsance of the <see cref="ViewModelExtension"/> class
|
||||||
|
/// </summary>
|
||||||
|
public ViewModelExtension()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When implemented in a derived class, returns an object that is provided as the
|
||||||
|
/// value of the target property for this markup extension.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceProvider">A service provider helper that can provide services for the markup extension.</param>
|
||||||
|
/// <returns>The object value to set on the property where the extension is applied.</returns>
|
||||||
|
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
var valueService = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
|
||||||
|
var targetObjectAsDependencyObject = valueService.TargetObject as DependencyObject;
|
||||||
|
if (targetObjectAsDependencyObject == null)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
return View.GetBindingToViewModel(targetObjectAsDependencyObject).ProvideValue(serviceProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,7 +41,7 @@
|
||||||
</TextBox.InputBindings>
|
</TextBox.InputBindings>
|
||||||
<TextBox.ContextMenu>
|
<TextBox.ContextMenu>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<MenuItem Header="Click Here" Command="{s:Action ShowActionTargetSaved}"/>
|
<MenuItem Header="{Binding Foo}" DataContext="{s:ViewModel}" Command="{s:Action ShowActionTargetSaved}"/>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</TextBox.ContextMenu>
|
</TextBox.ContextMenu>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace StyletIntegrationTests
|
||||||
{
|
{
|
||||||
private IWindowManager windowManager;
|
private IWindowManager windowManager;
|
||||||
|
|
||||||
|
public string Foo => "Foo";
|
||||||
|
|
||||||
public ShellViewModel(IWindowManager windowManager)
|
public ShellViewModel(IWindowManager windowManager)
|
||||||
{
|
{
|
||||||
this.windowManager = windowManager;
|
this.windowManager = windowManager;
|
||||||
|
|
Loading…
Reference in New Issue