如何验证列表框(MVVM)?

Posted

技术标签:

【中文标题】如何验证列表框(MVVM)?【英文标题】:How to validate a ListBox (MVVM)? 【发布时间】:2018-06-27 19:51:13 【问题描述】:

我希望我能正确解释我的代码和我的问题,因为它有很多代码。我们正在 Visual Studio 中制作图书管理器应用程序。模型 (Book.cs) 包含书籍的属性(作者、标题、发布等)。

在 MainWindow.xaml 中,我们有一个列表框,其中列出了所有书籍,左侧有对应于属性的标签和文本框(标签“作者”,文本框中显示作者的姓名)。使用此文本框,您可以更新属性。到目前为止,xaml 看起来也是如此:

<Window x:Class="BookApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:model="clr-namespace:BookApplication.Model"
        xmlns:viewModel="clr-namespace:BookApplication.ViewModel"
        mc:Ignorable="d"
        Title="Book-Manager" Height="350" Width="525">
    <Window.Resources>
        <model:Book x:Key="myBook" Title="Harry Potter und die Heiligtümer des Todes" Author="J.K.Rowling" Publisher="Carlsen" Edition="1" Release="01.01.2017" ></model:Book>
        <viewModel:BookCollectionViewModel x:Key="bcvm"></viewModel:BookCollectionViewModel>

    </Window.Resources>


    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="Datei">
                <MenuItem Header="Neu" Command="Binding NewClick, Mode=OneWay, Source=StaticResource bcvm" InputGestureText="Strg+N"/>
                <Separator/>
                <MenuItem x:Name="Open" Header="Öffnen" Command="Binding LoadClick, Mode=OneWay, Source=StaticResource bcvm" InputGestureText="Strg+O"/>
                <MenuItem x:Name="Save" Header="Speichern" Command="Binding SaveClick, Mode=OneWay, Source=StaticResource bcvm" InputGestureText="Strg+S"/>
                <MenuItem Header="Speichern unter..." Command="Binding SaveAsClick, Mode=OneWay, Source=StaticResource bcvm"/>

            </MenuItem>
            <MenuItem Header="Neues Buch" Command="Binding NewBookClick, Mode=OneWay, Source=StaticResource bcvm"></MenuItem>
        </Menu>


        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="252*"/>
                <ColumnDefinition Width="265*"/>
            </Grid.ColumnDefinitions>

            <Grid Grid.Column="0" Margin="-2,0,0,0">
                <Label x:Name="labelTitle" Content="Titel" HorizontalAlignment="Left" Margin="9,10,0,0" VerticalAlignment="Top" Width="46" Height="20" FontSize="8"/>
                <TextBox x:Name="textboxTitle" HorizontalAlignment="Left" Height="20" Margin="87,10,0,0" TextWrapping="Wrap" Text="Binding ElementName= BooksListBox, Path=SelectedItem.Title" VerticalAlignment="Top" Width="160" FontSize="8" />

                <Label x:Name="labelAuthor" Content="Autor" HorizontalAlignment="Left" Margin="10,30,0,0" VerticalAlignment="Top" Width="62" Height="22" FontSize="8"/>
                <TextBox x:Name="textboxAuthor" HorizontalAlignment="Left" Height="18" Margin="87,34,0,0" TextWrapping="Wrap"  Text="Binding ElementName=BooksListBox, Path=SelectedItem.Author" VerticalAlignment="Top" Width="160" FontSize="8" />

                <Label x:Name="labelPublisher" Content="Verlag" HorizontalAlignment="Left" Margin="10,52,0,0" VerticalAlignment="Top" Width="45" FontSize="8" Height="24"/>
                <TextBox x:Name="textboxPublisher" HorizontalAlignment="Left" Height="17" Margin="87,58,0,0" TextWrapping="Wrap" Text="Binding Source=StaticResource myBook, Path=Publisher, Mode=TwoWay" VerticalAlignment="Top" Width="160" FontSize="8" />

                <Label x:Name="labelEdition" Content="Auflage" HorizontalAlignment="Left" Margin="10,76,0,0" VerticalAlignment="Top" Width="45" FontSize="8" Height="26"/>
                <TextBox x:Name="textboxEdition" HorizontalAlignment="Left" Height="15" Margin="87,79,0,0" TextWrapping="Wrap" Text="Binding ElementName=BooksListBox, Path=SelectedItem.Edition" VerticalAlignment="Top" Width="160" FontSize="8" />

                <Label x:Name="labelRelease" Content="Veröffentlichung" HorizontalAlignment="Left" Margin="9,99,0,0" VerticalAlignment="Top" Width="102" FontSize="8"/>
                <TextBox x:Name="textboxRelease" HorizontalAlignment="Left" Height="14" Margin="87,103,0,0" TextWrapping="Wrap" Text="Binding ElementName=BooksListBox, Path=SelectedItem.Release, Mode=TwoWay" VerticalAlignment="Top" Width="160" FontSize="8" />

                <Label x:Name="labelISBN" Content="ISBN" HorizontalAlignment="Left" Margin="9,120,0,0" VerticalAlignment="Top" Width="81" FontSize="8"/>
                <TextBox x:Name="textboxISBN" HorizontalAlignment="Left" Height="14" Margin="87,125,0,0" TextWrapping="Wrap" Text="Binding ElementName=BooksListBox, Path=SelectedItem.Isbn" VerticalAlignment="Top" Width="160" FontSize="8" />

                <Button x:Name="buttonRemove" Command="Binding RemoveBookClick, Mode=OneWay, Source=StaticResource bcvm" Content="Löschen" HorizontalAlignment="Left" Margin="9,160,0,0" VerticalAlignment="Top" Width="75"/>
            </Grid>

            <ListBox Grid.Column="1" x:Name="BooksListBox" ItemsSource="Binding Source=StaticResource bcvm" SelectedItem="Binding Source=StaticResource bcvm, Path=SelectedBook">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock FontWeight="Bold" FontSize="12" Text="Binding Path=Title"/>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Binding Path=Author"></TextBlock>
                                <TextBlock Text=";  Auflage: "></TextBlock>
                                <TextBlock Text="Binding Path=Edition"></TextBlock>
                            </StackPanel>
                            <Canvas Width="50" Height="12" HorizontalAlignment="Left" >
                                <Ellipse Name="LeftEllipse" Height="10" Stroke="Black" Fill="Binding Path=ColorLeft" Width="10" Canvas.Left="10"/>
                                <Ellipse Name="MiddleEllipse" Height="10" Stroke="Black" Fill="Binding Path=ColorMiddle" Width="10" Canvas.Left="30"/>
                                <Ellipse Name="RightEllipse" Height="10" Stroke="Black" Fill="Binding Path=ColorRight" Width="10" Canvas.Left="50"/>
                            </Canvas>
                        </StackPanel>

                    </DataTemplate>
                </ListBox.ItemTemplate>

            </ListBox>

        </Grid>
    </DockPanel>

</Window>

如果您单击列表中的任何一本书,属性的值将在左侧的文本框中,您可以编辑这些值,正如我所说的。

现在我必须进行验证。问题是,我只能找到属性的直接验证,而我想验证 ListBox 的值和更新值。

我试过做这样的事情(来源:https://msdn.microsoft.com/es-es/library/ms753962,来源不是英文,但代码很清楚)。

我创建了一个如下所示的 AuthorRule 类:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace BookApplication.Model.Validation

    class AuthorRule : ValidationRule
    
        public AuthorRule()
        

        

        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        

           if (String.IsNullOrWhiteSpace((String)value))
           
                return new ValidationResult(false, "Dieser Feld ist verpflichtend.");
            
            else
            
                return new ValidationResult(true, null);
            

        
    

只是为了证明,如果字段为空。我的 Model 或 Book.cs 没有改变。 我的 MainWindow.xaml 看起来像这样(它就像另一段代码,但我只会显示 WindowResources 和 TextBox“作者”中的更改):

<Window.Resources>
        <model:Book x:Key="myBook" Title="Harry Potter und die Heiligtümer des Todes" Author="J.K.Rowling" Publisher="Carlsen" Edition="1" Release="01.01.2017" ></model:Book>
        <viewModel:BookCollectionViewModel x:Key="bcvm"></viewModel:BookCollectionViewModel>

        <ControlTemplate x:Key="validationTemplate">
            <DockPanel>
                <TextBlock Foreground="Red" FontSize="20">
                    <Run Text="!" />
                </TextBlock>
            </DockPanel>
        </ControlTemplate>

        <Style x:Key="textBoxInError" TargetType="x:Type TextBox">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" Value="Binding (Validation.Errors)[0].ErrorContent, RelativeSource=x:Static RelativeSource.Self" />
                </Trigger>
            </Style.Triggers>

        </Style>

    </Window.Resources>

上图是TextBox“作者”的截图,不知道路径和来源应该是什么,所以ListBox还是连着的!!

很抱歉这个问题太长了,有人可以帮我吗?

【问题讨论】:

【参考方案1】:

您已经在第一行设置了 Text 属性,并且再次将其设置回节点 TextBox.Text 中。您可能必须首先删除 Text 属性及其值。 此外,源字段不是必需的。 TextBox 的最终解决方案应该是这样的。

<TextBox x:Name="textboxAuthor" HorizontalAlignment="Left" Height="18" Margin="87,34,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Width="160" FontSize="8" Validation.ErrorTemplate="StaticResource validationTemplate" Style="StaticResource textBoxInError">
                    <TextBox.Text>
                        <Binding Path="SelectedItem.Author" ElementName="BooksListBox" UpdateSourceTrigger="PropertyChanged">
                            <Binding.ValidationRules>
                                <validation:AuthorRule/>
                            </Binding.ValidationRules>
                        </Binding>
                    </TextBox.Text>
                </TextBox>

这应该可行。

【讨论】:

以上是关于如何验证列表框(MVVM)?的主要内容,如果未能解决你的问题,请参考以下文章

WPF MVVM C#:列表框拖放而没有代码

WPF MVVM更新列表框

ObservableCollection刷新视图MVVM

实现动态/级联列表框的最佳方法[关闭]

MVVM:将带有 Observable 集合的命令绑定到 Listbox 并从文本框中获取值

使用 MVVM 重置组合框选定项目