我在模型对象上有一个只读属性,但想要双向绑定它。当绑定源应该更新时,我希望将更新“重定向”到一个单独的方法,该方法插入我们的控制器基础结构以执行更新并生成具有change属性的新模型。简单地在只读属性上使用Binding并向Binding.TargetUpdated
事件添加处理程序不起作用(抛出InvalidOperationException,指定该属性应该是只读的)。
有一个简单的解决方案(创建模型的副本,具有为我重定向的读写属性)但我真的不想复制我的所有模型对象。有没有办法以编程方式执行此操作?
答案 0 :(得分:1)
此解决方案基于自定义标记扩展,为某些依赖项属性设置双向绑定。绑定使用某种带有可写属性的包装器作为源。包装器调用基础结构代码以在属性更改后更新并生成新模型。
下面是带有硬编码方案的示例,但我认为这个想法很清楚。
namespace MyApp
{
public class MyModel
{
//readonly property
public string Name { get; private set; }
public MyModel(string name)
{
Name = name;
}
}
public class MyViewModel
{
public MyModel Model { get; set; }
public MyViewModel()
{
Model = new MyModel("default");
}
}
public class Wrapper
{
public MyViewModel ViewModel { get; set; }
//writable property to enable two-way binding
public object Value
{
get
{
return ViewModel.Model.Name;
}
set
{
//call your infrastructure method to
//update and generate new model
ViewModel.Model = new MyModel((string)value);
}
}
}
[MarkupExtensionReturnType(typeof(Object))]
public class CustomBinding : MarkupExtension
{
//you can add any properties here for your infrastructure method call
//public string PropertyName { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
var binding = new Binding()
{
//get whatever you need from target element to setup the binding and wrapper
Source = new Wrapper()
{
ViewModel = (provideValueTarget.TargetObject as FrameworkElement).DataContext as MyViewModel
},
Path = new PropertyPath("Value")
};
var result = binding.ProvideValue(serviceProvider);
return result;
}
}
}
XAML
<StackPanel>
<StackPanel.DataContext>
<MyApp:MyViewModel />
<StackPanel.DataContext>
<TextBox Text="{MyApp:CustomBinding}" />
<StackPanel />
答案 1 :(得分:0)
我不认为你会在没有绑定到新的中间对象的情况下逃脱。有些人可能称之为视图模型。我想知道自定义IValueConverter
是否能够在ConvertBack
中根据需要拦截写操作,但我有理由相信绑定系统甚至不会尝试调用转换器源属性不可写。
答案 2 :(得分:0)
我找到了一个使用ExpandoObject
的解决方案 - 给定一个只读模型对象,这会在运行时生成一个读写模型,只要有任何属性发生变化就会调用控制器方法:
public static dynamic GetAdapterFor(IController controller, object modelObj)
{
if (modelObj == null)
return null;
ExpandoObject obj = new ExpandoObject();
// add all the properties in the model
foreach (var prop in modelObj.GetType().GetProperties())
{
((IDictionary<string, object>)obj).Add(prop.Name, prop.GetValue(modelObj, null));
}
// add the handler to update the controller when a property changes
((INotifyPropertyChanged)obj).PropertyChanged += (s, e) => UpdateController(controller, e.PropertyName, ((IDictionary<string, object>)s)[e.PropertyName]);
return obj;
}
private static void UpdateController(IController controller, string propertyName, object propertyValue)
{
controller.SetPropertyValue(propertyName, propertyValue);
}