C# 验证列表框';s的内容不是空的
我的.NET 4.0 WPF应用程序有一个对话框,其中包含一个C# 验证列表框';s的内容不是空的,c#,wpf,xaml,mvvm,listbox,C#,Wpf,Xaml,Mvvm,Listbox,我的.NET 4.0 WPF应用程序有一个对话框,其中包含一个列表框。列表框最初为空。该对话框具有UI控件,允许用户将项目插入列表框。我使用的是MVVM,视图模型类实现了IDataErrorInfo。视图模型类中有一个绑定到列表框的ItemsSource属性的ObservableCollection 在用户至少在列表框中输入一项之前,我不想让用户单击“确定”。如果列表框为空,我希望在其周围显示一个红色边框&在其右侧显示一个错误图标,在列表框的工具提示中显示错误消息。这是我的应用程序中显示所有其他
列表框
。列表框最初为空。该对话框具有UI控件,允许用户将项目插入列表框
。我使用的是MVVM,视图模型类实现了IDataErrorInfo
。视图模型类中有一个绑定到列表框的ItemsSource属性的ObservableCollection
在用户至少在列表框
中输入一项之前,我不想让用户单击“确定”。如果列表框
为空,我希望在其周围显示一个红色边框&在其右侧显示一个错误图标,在列表框的
工具提示中显示错误消息。这是我的应用程序中显示所有其他错误的方式
以下是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>
这里有一个复杂的地方,即使这个列表框最初是空的,但是如果Type属性是一个值,那么它为空只是一个错误&子类型属性是SubType 1。最初,这些属性没有这些值。当第一次显示对话框时,我可以看到我的验证代码被调用,但是没有显示错误,因为列表框
甚至没有被显示
更改Type属性后,SubType属性将自动设置为SubType 1&将显示列表框
,并且该列表框为空。我已经能够验证是否调用了视图模型类的IDataErrorInfo
实现中的验证代码,并返回了一条错误消息,但没有显示错误模板。实际上,该选项卡上根本没有显示错误,我认为它与之前的选项卡相同,即显示错误
我对列表框做了什么错误
编辑
在选项卡上有一个文本框
,它最初也处于错误状态&没有显示错误消息。当我在其中输入一些文本时,验证代码运行了&它被标记为OK。然而,当我清空它时,它确实显示了一个错误条件。也许我的问题是我需要做点什么