Turn s:ViewModel into a proper binding, making it vaguely useful

This commit is contained in:
Antony Male 2015-09-24 15:43:06 +01:00
parent e8d9f95dfd
commit 7fe21eb40b
5 changed files with 109 additions and 43 deletions

View File

@ -131,7 +131,7 @@
<Compile Include="Xaml\View.cs" />
<Compile Include="ViewManager.cs" />
<Compile Include="WindowManager.cs" />
<Compile Include="Xaml\ViewModelExtension.cs" />
<Compile Include="Xaml\ViewModelBindingExtension.cs" />
</ItemGroup>
<ItemGroup>
<Page Include="MessageBoxView.xaml">

View File

@ -150,6 +150,15 @@ namespace Stylet.Xaml
view.Resources[ViewModelProxyResourceKey] = bindingProxy;
}
internal static void EnsureViewModelProxyValueSetUp(DependencyObject view)
{
if (view.GetValue(ViewModelProxyProperty) == null)
{
var resource = new DynamicResourceExtension(ViewModelProxyResourceKey).ProvideValue(null);
view.SetValue(ViewModelProxyProperty, resource);
}
}
/// <summary>
/// Fetch a binding which can be used to retrieve the ViewModel associated with a View
/// </summary>
@ -157,11 +166,7 @@ namespace Stylet.Xaml
/// <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);
}
EnsureViewModelProxyValueSetUp(view);
var binding = new Binding()
{
@ -174,7 +179,7 @@ namespace Stylet.Xaml
return binding;
}
private static readonly DependencyProperty ViewModelProxyProperty =
internal static readonly DependencyProperty ViewModelProxyProperty =
DependencyProperty.RegisterAttached("ViewModelProxy", typeof(BindingProxy), typeof(View), new PropertyMetadata(null));
/// <summary>

View File

@ -0,0 +1,96 @@
using System;
using System.Globalization;
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 ViewModelBindingExtension : MarkupExtension
{
/// <summary>
/// Gets or sets the path to the property
/// </summary>
public string Path { get; set; }
/// <summary>
/// Gets or sets the converter to use.
/// </summary>
public IValueConverter Converter { get; set; }
/// <summary>
/// Gets or sets the culture in which to evaluate the converter.
/// </summary>
public CultureInfo ConverterCulture { get; set; }
/// <summary>
/// Gets or sets the parameter to pass to the Converter.
/// </summary>
public object ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the value to use when the binding is unable to return a value
/// </summary>
public object FallbackValue { get; set; }
/// <summary>
/// Gets or sets a string that specifies how to format the binding if it displays the bound value as a string
/// </summary>
public string StringFormat { get; set; }
/// <summary>
/// Gets or sets the value that is used in the target when the value of the source is null
/// </summary>
public object TargetNullValue { get; set; }
/// <summary>
/// Instantiates a new instance of the <see cref="ViewModelBindingExtension"/> class
/// </summary>
public ViewModelBindingExtension()
{
}
/// <summary>
/// Initializes a new instance of the Binding class with an initial path
/// </summary>
/// <param name="path">The initial Path for the binding</param>
public ViewModelBindingExtension(string path)
{
this.Path = path;
}
/// <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;
View.EnsureViewModelProxyValueSetUp(targetObjectAsDependencyObject);
var binding = new Binding()
{
Source = targetObjectAsDependencyObject,
Path = new PropertyPath("(0).(1)." + this.Path, View.ViewModelProxyProperty, BindingProxy.DataProperty),
Mode = BindingMode.OneWay,
Converter = this.Converter,
ConverterCulture = this.ConverterCulture,
ConverterParameter = this.ConverterParameter,
FallbackValue = this.FallbackValue,
StringFormat = this.StringFormat,
TargetNullValue = this.TargetNullValue,
};
return binding.ProvideValue(serviceProvider);
}
}
}

View File

@ -1,35 +0,0 @@
using System;
using System.Windows;
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 instance 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);
}
}
}

View File

@ -51,7 +51,7 @@
<GroupBox DockPanel.Dock="Top" Header="{}{ViewModel}" Padding="10">
<DockPanel DataContext="{x:Null}">
<TextBlock DockPanel.Dock="Top">Ensure that the label below displays the text "Pass":</TextBlock>
<TextBlock DockPanel.Dock="Top" DataContext="{s:ViewModel}" Text="{Binding ViewModelTestLabel}"/>
<TextBlock DockPanel.Dock="Top" Text="{s:ViewModelBinding ViewModelTestLabel}"/>
</DockPanel>
</GroupBox>
</DockPanel>