我的.NET 4.0 WPF应用程序有一个包含ListBox
的对话框。 ListBox
最初为空。该对话框具有UI控件,允许用户将项目插入ListBox
。我正在使用MVVM,视图模型类实现IDataErrorInfo
。视图模型类中有ObservableCollection
绑定到ListBox's ItemsSource
属性。
我不想让用户点击确定,直到他们在ListBox
中输入至少一个项目为止。如果ListBox
为空,我想在其周围显示红色边框&右侧的错误图标,ListBox's
工具提示中显示错误消息。这是我的应用程序中显示所有其他错误的方式。
以下是XAML的编辑版本:
<Window x:Class="MyDialog class's type"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cs="clr-namespace:MyControlsDLL's namespace"
xmlns:vm="clr-namespace:My View Model DLL's namespace"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
DataContext="{Binding Path=SelectedCamera, RelativeSource={RelativeSource Self}}"
Loaded="MyDialog_Loaded"
SizeToContent="Height"
cs:ThemeSelector.CurrentThemeDictionary="{Binding Path=TimeOfDayTheme, RelativeSource={RelativeSource Self}}"
Width="800"
WindowStartupLocation="CenterOwner">
<cs:MyDialog.CommandBindings>
<cs:DataContextCommandBinding CanExecute="CanAddOrEditItem" Command="cs:MyCommands.AddItem" Executed="AddOrEditItem" />
<cs:DataContextCommandBinding CanExecute="CanRemoveItem" Command="cs:MyCommands.RemoveItem" Executed="RemoveItem" />
</cs:MyDialog.CommandBindings>
<cs:MyDialog.Resources>
<ResourceDictionary>
<BitmapImage x:Key="ErrorImage" UriSource="Resources/Error.png" />
<cs:BooleanInverter x:Key="NOT" />
<cs:BooleanToVisibilityConverter x:Key="BoolToVisibile" True="Visible" False="Collapsed" />
<cs:BooleanToVisibilityConverter x:Key="BoolToCollapsed" True="Collapsed" False="Visible" />
<cs:EnumToBooleanConverter x:Key="SubTypeConverter" />
<cs:MultiBoolConverter x:Key="BoolCombiner" />
<cs:EnumDisplayer ResourceManager="{x:Static res:MyApp.ResourceManager}" Type="{x:Type sys:DateTimeKind}" x:Key="DateTimeKinds">
<cs:EnumOverride EnumValue="Local" ResourceKey="DateKind_Local" />
<cs:EnumOverride EnumValue="Unspecified" ResourceKey="DateKind_Unspecified" />
<cs:EnumOverride EnumValue="Utc" ResourceKey="DateKind_Utc" />
</cs:EnumDisplayer>
<cs:EnumDisplayer ResourceManager="{x:Static res:MyApp.ResourceManager}" Type="{x:Type vm:MyTypes}" x:Key="MyTypes" />
<ControlTemplate x:Key="InputErrorTemplate">
<DockPanel LastChildFill="True">
<Image DockPanel.Dock="Right"
Height="20"
Margin="-30,0,0,0"
Source="{StaticResource ErrorImage}"
ToolTip="{x:Static res:MyApp.Common_InvalidData}"
VerticalAlignment="Center"
Width="20" />
<Border BorderBrush="Red"
BorderThickness="5"
Margin="5,5,30,5">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource InputErrorTemplate}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource InputErrorTemplate}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip">
<Setter.Value>
<Binding Path="(Validation.Errors).CurrentItem.ErrorContent" RelativeSource="{x:Static RelativeSource.Self}" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</cs:MyDialog.Resources>
<Grid Name="LayoutRoot" Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<AdornerDecorator Grid.Row="0">
<Grid Name="CommonRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Column1" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Column2" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Margin="5"
Text="Name:"
TextAlignment="Right"
VerticalAlignment="Center" />
<TextBox Grid.Column="1"
Grid.Row="0"
Margin="5,5,30,5"
MaxLength="50"
Name="NameBox"
TabIndex="0"
Text="{Binding Mode=TwoWay, NotifyOnSourceUpdated=True, Path=Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
TextAlignment="Left"
VerticalAlignment="Center" />
<TextBlock Grid.Column="3"
Grid.Row="0"
Margin="5"
Text="Type:"
TextAlignment="Right"
VerticalAlignment="Center" />
<ComboBox Grid.Column="4"
Grid.Row="0"
Height="50"
ItemsSource="{Binding Source={StaticResource MyTypes}, Path=DisplayNames}"
SelectedValue="{Binding Converter={StaticResource MyTypes}, Mode=TwoWay, Path=MyType}"
SelectionChanged="LPRTypePicker_SelectionChanged"
Margin="5"
x:Name="MyTypePicker"
TabIndex="1"
VerticalAlignment="Center" />
<TabControl Grid.Column="0"
Grid.ColumnSpan="6"
Grid.Row="1"
Name="Tabs">
<TabItem Header="Tab 1"
Name="Tab1">
<AdornerDecorator>
<Border BorderBrush="{DynamicResource ELDControlBorder}"
BorderThickness="0,0,0,1">
<Grid Background="{DynamicResource ELDContentBackground}"
Name="LPRConfiguration">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Column1" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="Auto" SharedSizeGroup="Column2" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<!-- Row definitions -->
</Grid.RowDefinitions>
<!-- Controls for this TabItem -->
</Grid>
</Border>
</AdornerDecorator>
</TabItem>
<TabItem Header="Directories List"
Visibility="{Binding Path=CanShow, Converter={StaticResource BoolToVisibile}}">
<AdornerDecorator>
<Border BorderThickness="0,0,0,1">
<Grid Name="Tab2Root">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" SharedSizeGroup="Column1" />
<ColumnDefinition Width="3.5*" />
<ColumnDefinition Width="110" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
HorizontalAlignment="Right"
Margin="5"
Text="Mode:" />
<Grid Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<RadioButton Content="SubType 1"
Grid.Column="1"
GroupName="SubTypes"
IsChecked="{Binding Converter={StaticResource SubTypeConverter}, ConverterParameter=SubType1, Path=SubType}"
Margin="5"
TabIndex="8"
VerticalAlignment="Center" />
<RadioButton Checked="SubType_Changed"
Content="Value2"
Grid.Column="3"
GroupName="SubTypes"
IsChecked="{Binding Converter={StaticResource SubTypeConverter}, ConverterParameter=SubType2, Path=SubType}"
Margin="5"
Name="SubTypeSubType2Button"
TabIndex="8"
VerticalAlignment="Center" />
</Grid>
<TextBlock Grid.Column="0"
Grid.Row="2"
HorizontalAlignment="Right"
Margin="5"
Text="An Item:"
VerticalAlignment="Center"
Visibility="{Binding Converter={StaticResource BoolToVisibile}, Path=SubTypeIsSubType1}" />
<Grid Grid.Column="1"
Grid.Row="2"
Visibility="{Binding Converter={StaticResource BoolToVisibile}, Path=SubTypeIsSubType1}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Margin="5"
Name="NewItemBox"
TabIndex="11"
Text="{Binding Mode=TwoWay, Path=NewItem, UpdateSourceTrigger=PropertyChanged}"
ToolTip="{x:Static res:MyApp.MyDialog_NewItem_Tooltip}"
VerticalAlignment="Center" />
<Button Command="cs:MyCommands.Browse"
Content="Browse..."
Grid.Column="1"
Height="35"
Margin="5"
Name="BrowseDirectoriesButton"
TabIndex="12" />
</Grid>
<Button Command="cs:MyCommands.AddItem"
Content="{Binding Path=AddOrEditLabel}"
Grid.Column="2"
Grid.Row="2"
Height="50"
Margin="5"
Name="AddItemButton"
TabIndex="13" />
<TextBlock Grid.Column="0"
Grid.Row="3"
HorizontalAlignment="Right"
Margin="5"
Text="Selected Items:"
TextAlignment="Right"
TextWrapping="WrapWithOverflow"
Visibility="{Binding Converter={StaticResource BoolToVisibile}, Path=SubTypeIsSubType1}" />
<ListBox Grid.Column="1"
Grid.Row="3"
Height="160"
ItemsSource="{Binding Path=SelectedItems}"
Margin="5,5,30,5"
Name="ItemsList"
SelectedValue="{Binding Mode=TwoWay, Path=SelectedItem, ValidatesOnDataErrors=True}"
TabIndex="14"
Visibility="{Binding Converter={StaticResource BoolToVisibile}, Path=SubTypeIsSubType1}" />
<Button Command="cs:MyCommands.RemoveItem"
Content="Remove"
Grid.Column="2"
Grid.Row="3"
Height="50"
Margin="5"
Name="RemoveItemButton"
TabIndex="15"
VerticalAlignment="Top"
Visibility="{Binding Converter={StaticResource BoolToVisibile}, Path=SubTypeIsSubType1}" />
</Grid>
</Border>
</AdornerDecorator>
</TabItem>
</TabControl>
</Grid>
</AdornerDecorator>
<!-- OK & Cancel Buttons here -->
</Grid>
</Window>
这里有一个复杂的问题,即使这个ListBox
最初是空的,如果Type属性是一个值&amp;,它只是一个错误。 SubType属性是SubType1。最初,这些属性没有这些值。首次显示对话框时,我可以看到我的验证代码被调用,但没有显示错误,因为甚至没有显示ListBox
。
更改Type属性后,SubType属性自动设置为SubType1&amp;显示ListBox
,它为空。我已经能够验证我的View Model类的IDataErrorInfo
实现中的验证代码是否被调用,并返回错误消息,但没有显示错误模板。实际上,该标签上根本没有显示任何错误,我认为它与之前的标签相同,显示错误。
我对ListBox
做错了什么?
修改
TextBox
上的Tab
最初也处于错误状态&amp;没有显示错误消息。当我在其中放入一些文本时,验证代码运行&amp;它被标记为OK。然而,当我清空它时,确实显示出错误状态。也许我的问题是,当用户选择Type1或更改SubType时,我需要做一些事情才能将其置于错误状态?
答案 0 :(得分:1)
我得到了它的工作。我将ValidatesOnDataErrors=True
添加到ItemsSource
上ListBox
属性的绑定中,现在显示错误模板。