Привязка к другому свойству связанного объекта в правиле валидации - PullRequest
0 голосов
/ 05 ноября 2019

Учитывая следующий пример View Model

public class MyViewModel
{
  public ObservableCollection<MyObjType> BoundItems { get; }
}

и MyObjType

public class MyObjType
{
  public string Name { get; set; }
  public int Id { get; set; }
}

Я добавил правило проверки в столбец DataGrid, где DataGrid привязан к BoundItems в моей ViewModel, а свойство Text в столбце шаблона связано с именем.

<DataGrid ItemsSource="{Binding BoundItems}">
      <DataGrid.Columns>
             <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TexBox>
                          <TextBox.Text>
                            <Binding Path="Name" ValidatesOnDataErrors="True">
                              <Binding.ValidationRules>
                                <xns:MyValidationRule>
                                  <xns:MyValidationRule.SomeDependencyProp>
                                    <xns:SomeDependencyProp SubProp={Binding Id} /> <!-- Not Working -->
                                  </xns:MyValidationRule.SomeDependencyProp>
                                </xns:MyValidationRule>
                              </Binding.ValidationRules>
                            </Binding>
                          </TextBox.Text>
                        </TextBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            ...
      </DataGrid.Columns>
</DataGrid>

Я хочу передать другое свойство Id моего типа коллекции (MyObjType) вПравило валидации, как мне получить к нему доступ из правила. Я знаю о заморозке и получении контекста модели представления, но мне нужно другое свойство моего типа коллекции, связанное с сеткой данных.

ValidationRule и SomeDependencyProp смоделированы по примеру, приведенному здесь: https://social.technet.microsoft.com/wiki/contents/articles/31422.wpf-passing-a-data-bound-value-to-a-validation-rule.aspx

public class SomeDependencyProp : DependencyObject
{
  public static readonly SubPropProperty =
     DependencyProperty.Register("SubProp", typeof(int),
     typeof(SomeDependencyProp), new FrameworkPropertyMetadata(0));

  public int SubProp{
    get { return (int)GetValue(SubPropProperty ); }
    set { SetValue(SubPropProperty, value); }
  }
}

public class MyValidationRule: System.Windows.Controls.ValidationRule
{
  public override ValidationResult Validate(object value, CultureInfo cultureInfo) 
  {
    ...
  }

  public SomeDependencyProp SomeDependencyProp { get; set; }
}

1 Ответ

1 голос
/ 05 ноября 2019

Вот ошибка, которую вы увидите, если посмотрите на панель «Вывод» в VS во время выполнения:

Не удается найти управляющий FrameworkElement или FrameworkContentElement для целевого элемента. BindingExpression: Path = Id;DataItem = NULL;целевым элементом является SomeDependencyProp (HashCode = 11898202);Свойство target - SubProp (тип Int32)

Добавьте трассировку к привязке ({Binding Id, PresentationTraceSources.TraceLevel=High}), и вы увидите много болтовни о "Framework ментора не найден". Проблема здесь в том, что правило проверки находится за пределами визуального дерева, поэтому DataContext не наследуется ни от одного из родителей (следовательно, DataItem = null в сообщении об ошибке выше). {Binding Id} терпит неудачу, потому что нет источника;он не знает, где искать свойство Id.

Каноническим решением этой ситуации является использование BindingProxy. Каноническим способом написания связующего прокси-класса является поиск в Google слова «stackoverflow bindingproxy wpf» или чего-то в этом роде и кража первого найденного вами .

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), 
                                    new UIPropertyMetadata(null));
}

Затем используйте так:

<TextBox>
    <TextBox.Resources>
        <local:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
    </TextBox.Resources>
    <TextBox.Text>
        <Binding Path="Name" ValidatesOnDataErrors="True">
            <Binding.ValidationRules>
                <xns:MyValidationRule>
                    <xns:MyValidationRule.SomeDependencyProp>
                        <xns:SomeDependencyProp 
                            SubProp="{Binding Data.Id, Source={StaticResource DataContextProxy}}" 
                            />
                    </xns:MyValidationRule.SomeDependencyProp>
                </xns:MyValidationRule>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Если вы создадите BindingProxy в DataTemplate.Resources вместо TextBox.Resources, вы снова получите «наставник фреймворка не найден». Выше работает для меня.

...