TextBox с проверкой теряет ErrorTemplate при смене вкладки - PullRequest
32 голосов
/ 28 марта 2012

У меня есть TextBox с правилом проверки, который находится на вкладке TabControl.ErrorTemplate по умолчанию правильно показывает (красная граница вокруг TextBox) при сбое правила проверки.
Однако, если есть переход на другую вкладку и затем обратно на вкладку с TextBox, подсветка ErrorTemplate пропала.Если в TextBox есть изменение, то правило проверки по-прежнему вызывается и возвращает false, но выделение ошибки по-прежнему не отображается.
Только когда текстовое содержимое изменяется, чтобы быть действительным, а затем снова недействительным, возвращается выделение.
Мне бы хотелось, чтобы, если текстовое содержимое было недопустимым, при переходе на другую вкладку и обратно сохраняется недопустимое выделение.Любые идеи, чтобы получить такое поведение, приветствуются.
XAML:

<TextBox Height="35" >
  <TextBox.Text>
    <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <ps:PanIdValidation />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

Ответы [ 3 ]

58 голосов
/ 28 марта 2012

TabItem должен быть определен следующим образом:

<TabItem Header="Foo">
    <Border>
        <AdornerDecorator>
            <Grid>
                <TextBox Height="35" >
                    <TextBox.Text>
                         <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged">
                             <Binding.ValidationRules>
                                 <ps:PanIdValidation />
                             </Binding.ValidationRules>
                          </Binding>
                      </TextBox.Text>
                  </TextBox>
              </Grid>
          </AdornerDecorator>
      </Border>
  </TabItem>

Проблема заключается в том, что сигналы ошибки отображаются в слое Adorner.При переключении вкладок этот слой отбрасывается.

11 голосов
/ 25 ноября 2015

Просто дополнение для особых случаев: у меня была похожая проблема, и сейчас я использую решение, подобное коду Дилана.

Разница в том, что мой TabItem содержит GroupBox и TextBox находится внутри него.В этом случае AdornerDecorator должен находиться в самом GroupBox, а не в качестве прямого потомка TabItem.

Так что это не сработало:

<TabItem>
    <AdornerDecorator>
        <Grid>
            <GroupBox>
                <Grid>
                    <TextBox>...<TextBox/>
                </Grid>
            </GroupBox>
        </Grid>
    </AdornerDecorator>
</TabItem>

Но это сработало:

<TabItem>
    <Grid>
        <GroupBox>
            <AdornerDecorator>
                <Grid>
                    <TextBox>...<TextBox/>
                </Grid>
            </AdornerDecorator>
        </GroupBox>
    </Grid>
</TabItem>

Я добавляю его, потому что я не мог легко найти решение, и даже документация AdornerLayer.GetAdornerLayer() (хотя и не уверена, применимо ли оно здесь) утверждает This static method traverses up the visual tree starting at the specified Visual and returns the first adorner layer found. - но, возможно, оно также останавливается в какой-то моментЭто не ясно из документов.

6 голосов
/ 24 января 2016

Как объяснил Дилан, это потому, что слой Adorner, в котором рисуются ошибки валидации, сбрасывается при переключении табуляции.Поэтому вам нужно обернуть содержимое с помощью AdornerDecorator.

. Я создал поведение , которое автоматически оборачивает Content из TabItem в AdornerDecorator, чтобы оно неэто не нужно делать вручную для всех TabItems.

public static class AdornerBehavior
{
    public static bool GetWrapWithAdornerDecorator(TabItem tabItem)
    {
        return (bool)tabItem.GetValue(WrapWithAdornerDecoratorProperty);
    }
    public static void SetWrapWithAdornerDecorator(TabItem tabItem, bool value)
    {
        tabItem.SetValue(WrapWithAdornerDecoratorProperty, value);
    }

    // Using a DependencyProperty as the backing store for WrapWithAdornerDecorator.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty WrapWithAdornerDecoratorProperty =
        DependencyProperty.RegisterAttached("WrapWithAdornerDecorator", typeof(bool), typeof(AdornerBehavior), new UIPropertyMetadata(false, OnWrapWithAdornerDecoratorChanged));

    public static void OnWrapWithAdornerDecoratorChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var tabItem = o as TabItem;
        if (tabItem == null) return;

        if(e.NewValue as bool? == true)
        {
            if (tabItem.Content is AdornerDecorator) return;
            var content = tabItem.Content as UIElement;
            tabItem.Content = null;
            tabItem.Content = new AdornerDecorator { Child = content };
        }
        if(e.NewValue as bool? == false)
        {
            if (tabItem.Content is AdornerDecorator)
            {
                var decorator= tabItem.Content as AdornerDecorator;
                var content = decorator.Child;
                decorator.Child = null;
                tabItem.Content = content;
            }
        }
    }
}

Вы можете установить это поведение для всех TabItems с помощью стиля по умолчанию:

<Style TargetType="TabItem">
    <Setter Property="b:AdornerBehavior.WrapWithAdornerDecorator" Value="True"></Setter>
</Style>

b - это пространство именгде находится поведение, примерно так (будет разным для каждого проекта):

xmlns:b="clr-namespace:Styling.Behaviors;assembly=Styling"
...