从 XAML 设置 ViewModel 属性值
我有一个在 XAML 中声明的视图(见下文)。关联的视图模型是使用 MEF 自动创建的。我希望能够执行以下操作:
<local:MyControl Owner={x:Static local:Owners.ProjectOwner} />
所需的最终效果是将某些视图模型属性设置为等于 Owners.ProjectOwner。
我可以使用 hacky 代码隐藏来实现所需的结果,但更愿意通过绑定或某种类似的方式来实现。谁能建议一种方法来做到这一点?
更新
我放弃了写一个行为。但我并没有将所有的努力仅仅针对一个特定的案例,而是通用了我的解决方案,并将其包含在下面,以防有人感兴趣。它是一种混合行为 (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());
}
}
}
然后像这样使用它:
<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>
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>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
任何 WPF 绑定的目标都必须是
DependencyProperty
。源可以是DependencyProperty
、实现INotifyPropertyChanged
的 CLR 对象,或者只是某个对象。可以通过更改Binding.Mode
属性来交换目标和源。但在这种情况下,绑定中的一项是静态解析的属性 (
Owners.ProjectOwner
)。因此,它不是DependencyProperty
。所以,它只能作为一个来源出现。因此,您将其绑定到的内容(目标)必须是DependencyProperty
。因此,它不能是视图模型上的属性(假设您尚未创建基于DependencyObject
的视图模型,这将是 一个错误)。因此,您不能直接将虚拟机上的属性绑定到静态属性。不过,您可以编写一个附加行为来为您完成此操作。
The target of any WPF binding must be a
DependencyProperty
. The source can be aDependencyProperty
, a CLR object that implementsINotifyPropertyChanged
, or just some object. The target and source can be swapped around by altering theBinding.Mode
property.But in this case one of the items in your binding is a statically-resolved property (
Owners.ProjectOwner
). Therefore, it's not aDependencyProperty
. Therefore, it can only appear as a source. Therefore, what you're binding it to (the target) must be aDependencyProperty
. Therefore, it cannot be a property on your view model (assuming you've not createdDependencyObject
-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.