0

モデル オブジェクトに読み取り専用プロパティがありますが、それに双方向でバインドしたいと考えています。バインディング ソースを更新する必要がある場合は、代わりに、コントローラー インフラストラクチャにプラグインして更新を実行し、change プロパティを使用して新しいモデルを生成する別のメソッドに更新を「リダイレクト」する必要があります。読み取り専用プロパティで Binding を使用し、Binding.TargetUpdatedイベントにハンドラーを追加するだけでは機能しません (プロパティを読み取り専用にするように指定すると、InvalidOperationException がスローされます)。

これには簡単な解決策があります (モデルのコピーを作成し、リダイレクトを行う読み取り/書き込みプロパティを使用します)、すべてのモデル オブジェクトをコピーする必要はありません。これをプログラムで行う方法はありますか?

4

3 に答える 3

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 />
于 2012-11-19T23:06:03.013 に答える
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);
}
于 2012-11-20T09:28:18.893 に答える
0

新しい中間オブジェクトにバインドせずに逃げることはできないと思います。ビューモデルと呼ぶ人もいます。で必要に応じてカスタムIValueConverterが書き込み操作を傍受できるかどうか疑問に思ってConvertBackいましたが、ソース プロパティが書き込み可能でない場合、バインディング システムはコンバーターを呼び出そうとさえしないと確信しています。

于 2012-11-19T12:40:21.347 に答える