GroupBox в ContentControl - поддержка IDataErrorInfo, реализуемая содержимым, связанным с ContentControl - PullRequest
7 голосов
/ 24 октября 2011

У меня есть ViewModel, которая представляет несколько опций и реализует IDataErrorInfo. Эта ViewModel действительна, только если выбран хотя бы один из этих параметров. Это связано с ContentControl. DataTemplate используется для визуализации ViewModel как GroupBox, содержащего ItemsControl. Другой DataTemplate визуализирует каждый параметр как CheckBox.

Что мне нужно сделать, чтобы ContentControl работал вместе с IDataErrorInfo и проверять действительность, когда флажок установлен или снят?


Код:

Переплет:

<ContentControl Content="{Binding GeneralInvoiceTypes, ValidatesOnDataErrors=True}"
                Margin="0,0,5,0" />

Шаблоны данных:

<DataTemplate DataType="{x:Type ViewModels:MultipleOptionsViewModel}">
  <GroupBox Header="{Binding Title}">
    <ItemsControl ItemsSource="{Binding Options}" />
  </GroupBox>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:OptionViewModel}">
  <CheckBox IsChecked="{Binding IsChecked}"
            Content="{Binding Name}"
            Margin="6,3,3,0" />
</DataTemplate>

Style:

<Style TargetType="{x:Type ContentControl}">
  <Style.Triggers>
    <Trigger Property="Validation.HasError"
             Value="true">
      <Setter Property="ToolTip"
              Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" />
    </Trigger>
  </Style.Triggers>
  <Setter Property="Validation.ErrorTemplate">
    <Setter.Value>
      <ControlTemplate>
        <Grid>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="90*" />
            <ColumnDefinition Width="20" />
          </Grid.ColumnDefinitions>
          <Border BorderBrush="Red"
                  BorderThickness="1"
                  CornerRadius="2.75"
                  Grid.Column="0">
            <AdornedElementPlaceholder Grid.Column="0" />
          </Border>
          <TextBlock Foreground="Red"
                     Grid.Column="1"
                     Margin="0"
                     FontSize="12"
                     VerticalAlignment="Center"
                     HorizontalAlignment="Left"
                     x:Name="txtError">
            *
          </TextBlock>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Ответы [ 3 ]

3 голосов
/ 09 ноября 2011

Что мне нужно сделать, чтобы ContentControl работал вместе с IDataErrorInfo и проверять действительность, когда флажок установлен или снят?

Добавление бита к Рейчелс ответ.

Эту проблему было бы легче решить с помощью асинхронной проверки данных, но, к сожалению, она не доступна до WPF 4.5 выпущен .

Content привязывается к GeneralInvoiceTypes в MainViewModel.Так как мы не можем выполнить асинхронную проверку данных, то PropertyChanged необходимо повысить до GeneralInvoiceTypes, чтобы проверка прошла.Это сработало бы, но я бы использовал подход, предложенный Рэйчел , и ввел бы другое свойство под названием IsValid в MultipleOptionsViewModel

Привязка к IsValid может быть выполнена изTag (или прикрепленное свойство) к GeneralInvoiceTypes.IsValid.Мы также должны получать уведомления в MultipleOptionsViewModel , когда IsChecked изменяется в любом из Options.Это можно сделать, например, с помощью привязки команды в CheckBoxes.

Поэтому потребуются некоторые изменения в следующих строках.

Я также загрузил пример проекта сэто реализовано здесь: https://www.dropbox.com/s/fn8e4n4s68wj3vk/ContentControlValidationTest.zip?dl=0

ContentControl

<ContentControl Content="{Binding Path=GeneralInvoiceTypes}"
                Tag="{Binding Path=GeneralInvoiceTypes.IsValid,
                              ValidatesOnDataErrors=True}" />

OptionViewModel DataTemplate

<DataTemplate DataType="{x:Type ViewModels:OptionViewModel}">
    <CheckBox IsChecked="{Binding IsChecked}"
                Content="{Binding Name}"
                Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContentControl}},
                                Path=DataContext.IsValidCheckCommand}"
                Margin="6,3,3,0" />
</DataTemplate>

MultipleOptionsViewModel

private ICommand m_isValidCheckCommand;
public ICommand IsValidCheckCommand
{
    get
    {
        return m_isValidCheckCommand ??
            (m_isValidCheckCommand = new RelayCommand(param => IsValidCheck()));
    }
}

private void IsValidCheck()
{
    IsValid = CheckIsValid();
}

private bool CheckIsValid()
{
    foreach (OptionViewModel option in Options)
    {
        if (option.IsChecked == true)
        {
            return true;
        }
    }
    return false;
}

private bool m_isValid;
public bool IsValid
{
    get { return m_isValid; }
    set
    {
        m_isValid = value;
        OnPropertyChanged("IsValid");
    }
}

public string this[string columnName]
{
    get
    {
        if (columnName == "IsValid")
        {
            if (IsValid == false)
            {
                return "At least 1 Option must be selected";
            }
        }
        return string.Empty;
    }
}
1 голос
/ 08 ноября 2011

Реализует ли класс, содержащий свойство GeneralInvoiceTypes, IDataErrorInfo?

Настройка ValidatesOnDataErrors=True будет отображать ошибки проверки для DataContext, который содержит привязанное свойство, поэтому в этом случае это действительно ParentViewModel.GetValidationError("GeneralInvoiceTypes"), а не GeneralInvoiceTypes.GetValidationError()

В этом случае вы можете либо добавить IDataErrorInfo к вашему ParentViewModel и проверить свойство GeneralInvoiceTypes, вернув его ошибку проверки, например:

public string GetValidationError(string propertyName)
{
    if (propertyName == "GeneralInvoiceTypes")
        return GeneralInvoiceTypes.GetValidationError();

    return null;
}

или вы можете создать IsValid свойство на GeneralInvoiceTypes, которое возвращает GetValidationError() == null и основывать ваш триггер проверки на {Binding IsValid} вместо Validation.HasError

0 голосов
/ 10 ноября 2011

Основываясь на данных ответах, я решил это так:

  1. Измените DataTemplate MultipleOptionsViewModel, чтобы связать свойство Options с ValidatesOnDataErrors=True:

    <DataTemplate DataType="{x:Type ViewModels:MultipleOptionsViewModel}">
        <GroupBox Header="{Binding Title}">
            <ItemsControl ItemsSource="{Binding Options,
                                        ValidatesOnDataErrors=True}"/>
        </GroupBox>
    </DataTemplate>
    
  2. Измените стиль ошибки на цель ItemsControl вместо ContentControl.

  3. Убедитесь, что MultipleOptionsViewModel вызывает PropertyChanged для "Options", когда один из дочерних параметров установлен или не отмечен
  4. Убедитесь, что MultipleOptionsViewModel реагирует на столбец "Options" в его реализации, если IDataErrorInfo.Item, т.е. в его индексаторе.

Преимущество этого решения в том, что оно использует поведение по умолчанию IDataErrorInfo, т. Е. Потребителю этой ViewModel не нужно обрабатывать его специальным образом.

Я знаю, что это решение не на 100% эквивалентно тому, о котором я спрашивал в вопросе - шаблон ошибки теперь отображается внутри вместо группового поля группы, но это то, с чем я могу жить .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...