我有一个带有验证规则的TextBox,它位于TabControl的选项卡上。当验证规则失败时,默认的ErrorTemplate会正确显示(TextBox周围的红色边框) 但是,如果切换到另一个选项卡,然后使用TextBox返回选项卡,则ErrorTemplate hightlight将消失。如果TextBox中有更改,则仍会调用验证规则并返回false,但仍未显示错误突出显示 只有当文本内容被更改为有效然后再次无效时才会重新亮相 我希望如果文本内容无效,切换到另一个选项卡并返回保持无效突出显示。任何想要获得这种行为的想法都是最受欢迎的 xaml:
<TextBox Height="35" >
<TextBox.Text>
<Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ps:PanIdValidation />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
答案 0 :(得分:58)
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>
问题是,Validation.Error提示是在Adorner Layer中绘制的。切换选项卡时,该图层将被丢弃。
答案 1 :(得分:11)
只是特殊情况的补充:我遇到了类似的问题,现在我正在使用类似于Dylan代码的解决方案。
不同之处在于我的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.
- 但也许它也停留在某些地方从文档中可以看出这一点。
答案 2 :(得分:6)
正如Dylan解释的那样,这是因为在Tab键切换时会丢弃其中绘制了验证错误的Adorner层。因此,您需要使用return "nothing".equalsIgnoreCase(message) ? "Are you sure?" :
"yes".equalsIgnoreCase(message) ? "ok" : null;
/* if message == "nothing" return "Are you sure?"
if message == "yes" return "ok"
if message != "yes" AND message != "nothing" return null
P.S: '==' and '!=' are for demonstration only. Keep using the .equals()
method for matching strings */
包装内容。
我创建了一个行为,会自动将AdornerDecorator
Content
包裹在TabItem
中,这样就不必完成在所有TabItems上手动操作。
AdornerDecorator
您可以通过默认样式在所有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