Wpf 从XAML设置ViewModel属性值

Wpf 从XAML设置ViewModel属性值,wpf,xaml,mvvm,properties,mef,Wpf,Xaml,Mvvm,Properties,Mef,我有一个用XAML声明的视图(见下文)。关联的视图模型是使用MEF自动创建的。我希望能够做到以下几点: <local:MyControl Owner={x:Static local:Owners.ProjectOwner} /> <local:MyControl> <i:Interaction.Behaviors> <bb:SetViewModelPropertyBehavior PropertyName="Owner" Pro

我有一个用XAML声明的视图(见下文)。关联的视图模型是使用MEF自动创建的。我希望能够做到以下几点:

<local:MyControl Owner={x:Static local:Owners.ProjectOwner} />
<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>

所需的净效果是将某些视图模型属性设置为等于owner.ProjectOwner

我可以使用黑客代码实现所需的结果,但更愿意通过绑定或类似的方式来实现。有人能建议一种方法吗

更新

我甘心写一篇文章。但是,我并没有把所有的努力仅仅放在一个特定的案例上,而是将我的解决方案泛化,并将其包括在下面,以防有人感兴趣。它是一种混合行为(System.Windows.Interactivity.dll),但也可能是一种传统的附加行为

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());
        }
    }
}
使用系统;
使用System.Windows;
使用System.Windows.Interactive;
命名空间BlendBehaviors
{
公共类SetViewModelPropertyBehavior:行为
{
公共静态只读DependencyProperty PropertyNameProperty=
Register(“PropertyName”、typeof(string)、typeof(SetViewModelPropertyBehavior));
公共静态只读DependencyProperty属性ValueProperty=
Register(“PropertyValue”、typeof(object)、typeof(SetViewModelPropertyBehavior));
public SetViewModelPropertyBehavior()
{ }
公共字符串PropertyName
{
get{return(string)GetValue(PropertyNameProperty);}
set{SetValue(PropertyNameProperty,value);}
}
公共对象属性值
{
get{return GetValue(PropertyValueProperty);}
set{SetValue(PropertyValue属性,值);}
}
受保护的覆盖无效附加()
{
base.onatached();
var ao=关联对象;
SetViewModel(ao.DataContext);
ao.DataContextChanged+=框架元素_DataContextChanged;
}
附加时受保护的覆盖无效()
{
base.OnDetaching();
AssociatedObject.DataContextChanged-=FrameworkElement\u DataContextChanged;
}
私有void FrameworkElement_DataContextChanged(对象发送方,DependencyPropertyChangedEventArgs e)
{
SetViewModel(例如NewValue);
}
私有void SetViewModel(对象viewModel)
{
SetViewModelProperty(viewModel、PropertyName、PropertyValue);
}
私有静态void SetViewModelProperty(对象viewModel、字符串propertyName、对象propertyValue)
{
如果(viewModel==null | | propertyName==null){
返回;
}
var info=viewModel.GetType().GetProperty(propertyName);
if(info!=null&&CanAssignValue(propertyValue,info.PropertyType)){
info.SetValue(viewModel,propertyValue,null);
}
}
私有静态bool CanAssignValue(对象值,类型targetType)
{
如果(值==null){
return!targetType.IsValueType | | Nullable.GetUnderlyingType(targetType)!=null;
}
返回targetType.IsAssignableFrom(value.GetType());
}
}
}
然后像这样使用它:

<local:MyControl Owner={x:Static local:Owners.ProjectOwner} />
<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>

任何WPF绑定的目标必须是
从属属性。源可以是
dependencProperty
、实现
INotifyPropertyChanged
的CLR对象,也可以只是某个对象。通过更改
Binding.Mode
属性,可以交换目标和源

但在本例中,绑定中的一个项是静态解析属性(
owner.ProjectOwner
)。因此,它不是一个
依赖属性
。因此,它只能作为源出现。因此,您要将其绑定到(目标)的内容必须是
dependencProperty
。因此,它不能是视图模型上的属性(假设您没有创建基于
DependencyObject
的视图模型,这将是)


因此,您不能直接将VM上的属性绑定到静态属性。你可以写一个附加的行为来帮助你。

我理解并同意你所说的一切。我不一定要在MyControl上声明一个名为“Owner”的DependencyProperty——示例代码只是说明性的。我可以编写一个附加的行为,但希望在第一个实例中,在不使用视图模型的强类型引用的情况下解决这个问题。我最终实现了一个混合行为:查看我的更新。谢谢你的贡献。