.net 双向绑定到只读属性
我在模型对象上有一个只读属性,但希望双向绑定到它。当绑定源应该更新时,我希望将更新“重定向”到一个单独的方法,该方法插入到控制器基础结构中,以执行更新并生成具有change属性的新模型。简单地在只读属性上使用绑定并将处理程序添加到.net 双向绑定到只读属性,.net,wpf,data-binding,.net,Wpf,Data Binding,我在模型对象上有一个只读属性,但希望双向绑定到它。当绑定源应该更新时,我希望将更新“重定向”到一个单独的方法,该方法插入到控制器基础结构中,以执行更新并生成具有change属性的新模型。简单地在只读属性上使用绑定并将处理程序添加到Binding.TargetUpdated事件不起作用(引发InvalidOperationException,指定属性应为只读) 有一个简单的解决方案(创建一个模型副本,使用读写属性为我重定向),但我并不想复制所有的模型对象。有没有一种方法可以通过编程来实现这一点?我
Binding.TargetUpdated
事件不起作用(引发InvalidOperationException,指定属性应为只读)
有一个简单的解决方案(创建一个模型副本,使用读写属性为我重定向),但我并不想复制所有的模型对象。有没有一种方法可以通过编程来实现这一点?我认为如果不绑定到新的中间对象,您将无法逃脱。有些人可能会称之为视图模型。我想知道定制的
IValueConverter
是否能够根据您在ConvertBack
中的需要拦截写入操作,但我可以合理地确定,如果源属性不可写,绑定系统甚至不会尝试调用转换器。此解决方案基于自定义标记扩展,它为某些依赖属性设置双向绑定。绑定使用某种具有可写属性的包装器作为源。包装器在属性更改后调用基础结构代码来更新和生成新模型
下面是一个硬编码场景的示例,但我认为这个想法非常清楚
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
我找到了一个使用
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);
}
公共静态动态GetAdapter(IController控制器,对象模型OBJ)
{
if(modelObj==null)
返回null;
ExpandoObject obj=新的ExpandoObject();
//在模型中添加所有属性
foreach(modelObj.GetType().GetProperties()中的var prop)
{
添加(prop.Name,prop.GetValue(modelObj,null));
}
//添加处理程序以在属性更改时更新控制器
((INotifyPropertyChanged)obj).PropertyChanged+=(s,e)=>UpdateController(controller,e.PropertyName,((IDictionary)s)[e.PropertyName]);
返回obj;
}
私有静态void UpdateController(IController控制器、字符串propertyName、对象propertyValue)
{
controller.SetPropertyValue(propertyName,propertyValue);
}
我不明白。您是否将视图直接绑定到模型,而不使用中间层?您从何处获取组合框的ItemsSource(例如)?组合框的所有值都由控制器生成。我通常会在任何集合和DataContext
之间放置一个ICollectionView
,这样我就可以收到更改通知并回拨控制器。
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);
}