Setting a ViewModel Property Value From XAML
I have a view that is declared in XAML (see below). The associated view-model is created automatically using MEF. I want to be able to do something like this:
<local:MyControl Owner={x:Static local:Owners.ProjectOwner} />
The desired net effect is for some view-model property to be set equal to Owners.ProjectOwner.
I can achieve the required result using hacky code-behind but would rather do this through bindings or some similar manner. Can anyone suggest a way of doing this?
UPDATE
I resigned myself to writing a behaviour. But rather than put in all the effort solely for one specific case, I have genericised my solution and I include it below in case anyone's interested. It's a Blend behaviour (System.Windows.Interactivity.dll) but could just as easily be a conventional attached behaviour.
using System;
using System.Windows;
using System.Windows.Interactivity;
namespace BlendBehaviors
{
public class SetViewModelPropertyBehavior : Behavior<FrameworkElement>
{
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.Register("PropertyName", typeof(string), typeof(SetViewModelPropertyBehavior));
public static readonly DependencyProperty PropertyValueProperty =
DependencyProperty.Register("PropertyValue", typeof(object), typeof(SetViewModelPropertyBehavior));
public SetViewModelPropertyBehavior()
{ }
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
public object PropertyValue
{
get { return GetValue(PropertyValueProperty); }
set { SetValue(PropertyValueProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
var ao = AssociatedObject;
SetViewModel(ao.DataContext);
ao.DataContextChanged += FrameworkElement_DataContextChanged;
开发者_运维技巧 }
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.DataContextChanged -= FrameworkElement_DataContextChanged;
}
private void FrameworkElement_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
SetViewModel(e.NewValue);
}
private void SetViewModel(object viewModel)
{
SetViewModelProperty(viewModel, PropertyName, PropertyValue);
}
private static void SetViewModelProperty(object viewModel, string propertyName, object propertyValue)
{
if (viewModel == null || propertyName == null) {
return;
}
var info = viewModel.GetType().GetProperty(propertyName);
if (info != null && CanAssignValue(propertyValue, info.PropertyType)) {
info.SetValue(viewModel, propertyValue, null);
}
}
private static bool CanAssignValue(object value, Type targetType)
{
if (value == null) {
return !targetType.IsValueType || Nullable.GetUnderlyingType(targetType) != null;
}
return targetType.IsAssignableFrom(value.GetType());
}
}
}
Then use it like this:
<local:MyControl>
<i:Interaction.Behaviors>
<bb:SetViewModelPropertyBehavior PropertyName="Owner" PropertyValue="{x:Static local:Owners.ProjectOwner}" />
<bb:SetViewModelPropertyBehavior PropertyName="AnotherProperty" PropertyValue="{StaticResource MyResourceKey}" />
</i:Interaction.Behaviors>
</local:MyControl>
The target of any WPF binding must be a DependencyProperty
. The source can be a DependencyProperty
, a CLR object that implements INotifyPropertyChanged
, or just some object. The target and source can be swapped around by altering the Binding.Mode
property.
But in this case one of the items in your binding is a statically-resolved property (Owners.ProjectOwner
). Therefore, it's not a DependencyProperty
. Therefore, it can only appear as a source. Therefore, what you're binding it to (the target) must be a DependencyProperty
. Therefore, it cannot be a property on your view model (assuming you've not created DependencyObject
-based view models, which would be a mistake).
So, you cannot directly bind a property on your VM to the static property. You could write an attached behavior that does this for you though.
精彩评论