2

コントロール (GridView以下の例で名前が付けられています) とビュー モデルがあり、それらはプロパティで双方向バインディングによってバインドされていSelectedValueます。GridViewを使用して、コントロール内のそのプロパティの特定の値を禁止したいと考えていますCoerceValueCallback

バインディングがその無効な値 (この例では 42) を にプッシュするDependencyPropertyと、CoerceValueメソッドはその値を破棄し、コントロールは以前の値を期待どおりに保持します。残念ながら、DependencyPropertyバインディングは変更されていないため、ビュー モデルのソース値は更新されません。

プロパティ値の変更をキャンセルするときに、ビュー モデルのプロパティをコントロールの値で更新するにはどうすればよいですか?

class ViewModel : INotifyPropertyChanged
{
    public int? SelectedValue
    {
        get { return _selectedValue; }
        set
        {
            _selectedValue = value;
            RaisePropertyChanged("SelectedValue");
        }
    }
    private int? _selectedValue;

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string name)
    {
        var propertyChanged = PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

class GridView : FrameworkElement
{
    public object SelectedValue
    {
        get { return (object)GetValue(SelectedValueProperty); }
        set { SetValue(SelectedValueProperty, value); }
    }

    public static readonly DependencyProperty SelectedValueProperty =
        DependencyProperty.Register("SelectedValue", typeof(object), typeof(GridView),
            new PropertyMetadata(null, null, new CoerceValueCallback(CoerceValue)));

    private static object CoerceValue(DependencyObject d, object baseValue)
    {
        // forbid value 42 and return previous value
        if (Equals(baseValue, 42))
        {
            return d.GetValue(GridView.SelectedValueProperty);
        }
        return baseValue;
    }
}

    [STAThread]
    static void Main()
    {
        ViewModel vm = new ViewModel();
        GridView grid = new GridView();
        Binding binding =  new Binding("SelectedValue") { Source = vm, Mode = BindingMode.TwoWay };
        grid.SetBinding(GridView.SelectedValueProperty, binding);

        vm.SelectedValue = 12;
        Console.WriteLine("should be 12: {0} = {1}", grid.SelectedValue, vm.SelectedValue);
        grid.SelectedValue = 23;
        Console.WriteLine("should be 23: {0} = {1}", grid.SelectedValue, vm.SelectedValue);
        vm.SelectedValue = 42;
        Console.WriteLine("should still be 23: {0} = {1}", grid.SelectedValue, vm.SelectedValue);
    }

私が試してみました

    var binding = BindingOperations.GetBindingExpression(d,
                      GridView.SelectedValueProperty);
    binding.UpdateSource();

CoerceValue役に立たない方法で。Dispatcher.CurrentDispatcher.BeginInvoke(updateSource, DispatcherPriority.DataBind);同様のスタックオーバーフローの質問で提案された前の2行も試しました。

他のアイデアはありますか?

更新

@Berrylは、ビューモデルでそのロジックを実行する必要があることを提案しました。私は彼に同意します...私ができないことを除いて。上記のコードで単純化しようとした完全な使用例について説明します。

GridView は私のコントロールではなく、継承元のサードパーティ データ グリッドです。ユーザーがレコードを編集すると、ユーザーがレコードを編集するウィンドウがポップアップ表示され、ビュー モデルがデータを更新し、ID を使用して古いレコードを再選択します。ユーザーがグリッドのフィルタリング機能を使用するまで、すべてがうまく機能します。フィルターが原因でレコードの編集によって行が非表示になる場合は、次のようになります。

  1. ValueListプロパティでモデル セットを表示する
  2. グリッドはその新しいリストを読み取ります
  3. グリッドはリストをフィルタリングします
  4. View Model はSelectedValueValueList
  5. グリッドはその新しいSelectedValueものを読み取りますが、フィルターで除外されているため破棄します
  6. グリッドには SelectedValue = 23 があり、ビュー モデルには SelectedValue = 42 があります
  7. ユーザーは、選択した行 (23 が表示されます) をダブルクリックして、レコードを編集します。
  8. ICommandダブルクリックにマッピングされた はビューモデルで呼び出されます
  9. SelectedValueビューモデルは、そのプロパティからのレコードで編集ウィンドウを起動します
  10. ユーザーはレコード 42 を編集しますが、それは 23 だと思います。
4

1 に答える 1