2012-11-19 90 views
0

我在模型對象上有一個只讀屬性,但想要雙向綁定它。當綁定源應該更新時,我希望更新被重定向到一個單獨的方法,該方法插入到我們的控制器基礎架構中以執行更新並生成具有更改屬性的新模型。簡單地在只讀屬性上使用Binding並向Binding.TargetUpdated事件添加處理程序不起作用(拋出InvalidOperationException,指定該屬性應該是隻讀的)。將雙向綁定到只讀屬性

有一個簡單的解決方案(創建一個模型的副本,具有讀寫屬性,爲我做重定向),但我不想複製所有模型對象。有沒有辦法以編程方式做這件事?

+0

我不明白。你是否將視圖直接綁定到沒有中間模型的模型上?你從哪裏得到組合框的ItemsSource(例如)? –

+0

組合框的所有值都由控制器生成。我通常在任何集合和'DataContext'之間放置一個'ICollectionView',這樣我就可以收到變化的通知並回復給控制器。 – thecoop

回答

0

我不認爲你會逃脫,沒有綁定到一個新的中間對象。有些人可能稱之爲視圖模型。我想知道自定義的IValueConverter是否可以攔截寫入操作,因爲您需要在ConvertBack,但我相當確信,如果源屬性不可寫,綁定系統甚至不會嘗試調用轉換器。

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 /> 
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); 
}