将双向绑定到只读属性

时间:2012-11-19 12:34:41

标签: .net wpf data-binding

我在模型对象上有一个只读属性,但想要双向绑定它。当绑定源应该更新时,我希望将更新“重定向”到一个单独的方法,该方法插入我们的控制器基础结构以执行更新并生成具有change属性的新模型。简单地在只读属性上使用Binding并向Binding.TargetUpdated事件添加处理程序不起作用(抛出InvalidOperationException,指定该属性应该是只读的)。

有一个简单的解决方案(创建模型的副本,具有为我重定向的读写属性)但我真的不想复制我的所有模型对象。有没有办法以编程方式执行此操作?

3 个答案:

答案 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);
}