【问题标题】:TreeView child nodes - input validation in wpfTreeView 子节点 - wpf 中的输入验证
【发布时间】:2013-07-29 10:23:45
【问题描述】:

我有一个像下面这样的树形视图。

  • R1
    • P1
      • 孩子 1
    • P2
      • 孩子 1
    • XX
      • 孩子 3

说我想显示错误消息 a) 树节点或 b) 它的容器

基于我的验证规则的值。

例如,如果我为这棵树应用验证逻辑,

验证规则>>>所有以“p”开头的父母都是有效的

因此,我的“xx”节点有效。我需要突出显示这个元素并通知用户。

我在 WPF 中使用 MVVM。我该怎么做。请帮助我。

【问题讨论】:

  • 请向我们展示您的尝试,然后根据您的代码,我们可以帮助您/向您展示您所犯的错误。
  • 我不熟悉 wpf 中的样式触发器/数据验证。我不知道该怎么做...

标签: wpf validation wpftoolkit wpf-4.0 hierarchicaldatatemplate


【解决方案1】:

我过去做过类似的事情,你可以这样做:

  1. 创建一个代表每个树节点的 ViewModel(例如 NodeViewModel),以防您还没有一个节点
  2. 您可以在该 NodeViewModel 中创建和实现一个接口,例如 IValidate,它公开一个 bool IsValid {get;set;} 属性。 (确保在 ViewModel 属性中实现 INotifyPropertyChanged
  3. 为您在视图中为节点(例如按钮)使用的控件类型创建一个样式,并创建一个样式触发器(网上有很多示例),当 IsValid 为 false 时将节点背景设置为红色并将其应用于你的控制:

                    <HierarchicalDataTemplate DataType="{x:Type ViewModel:NodeViewModel}" ItemsSource="{Binding Children}">
                        <Button Style="{StaticResource MyNodeStyle}" 
                                Content="{Binding Path=.}"
                    </HierarchicalDataTemplate>
    
  4. 在您的业务逻辑层或容器 ViewModel 创建一个方法,以递归方式验证树中的所有节点并将 IsValid 属性设置为所有节点,或在 IsValid Get 中定义您的业务规则(使其读取-只要)。当验证发生时,如果您的节点根据您的规则无效,它们将自动变为红色。

希望这会有所帮助,如果您有任何问题,请告诉我。

编辑:添加了一些示例类来说明解决方案。我的业务规则是,任何以“p”开头的节点都是有效的(绿色),无论它是否是父节点,但你明白了..

public class TreeNodeViewModel : ViewModelBase<TreeNodeViewModel>
{
    private string _NodeText;
    private ObservableCollection<TreeNodeViewModel> _Children;

    public TreeNodeViewModel()
    {
        Children = new ObservableCollection<TreeNodeViewModel>();
    }

    public string NodeText
    {
        get { return _NodeText; }
        set
        {
            _NodeText = value;
            NotifyPropertyChanged(m => m.NodeText);
            NotifyPropertyChanged(m => m.IsValid);
        }
    }

    public bool IsValid
    {
        get { return !string.IsNullOrEmpty(NodeText) && NodeText.ToLower().StartsWith("p"); }
    }

    public ObservableCollection<TreeNodeViewModel> Children
    {
        get { return _Children; }
        set
        {
            _Children = value;
            NotifyPropertyChanged(m => m.Children);
        }
    }
}

public class TreeViewModel : ViewModelBase<TreeViewModel>
{
    private ObservableCollection<TreeNodeViewModel> _RootNodeContainer;
    private TreeNodeViewModel _RootNode;

    public TreeViewModel()
    {
        InitializeTree();
    }

    private void InitializeTree()
    {
        RootNodeContainer = new ObservableCollection<TreeNodeViewModel>();
        RootNode = new TreeNodeViewModel() { NodeText = "R1" };
        RootNodeContainer.Add(RootNode);

        TreeNodeViewModel p1 = new TreeNodeViewModel() {NodeText = "P1"};
        p1.Children.Add(new TreeNodeViewModel(){NodeText = "Child1"});
        RootNode.Children.Add(p1);
        TreeNodeViewModel p2 = new TreeNodeViewModel() {NodeText = "P2"};
        p2.Children.Add(new TreeNodeViewModel() { NodeText = "Child1" });
        RootNode.Children.Add(p2);
        TreeNodeViewModel xx = new TreeNodeViewModel() {NodeText = "XX"};
        xx.Children.Add(new TreeNodeViewModel() { NodeText = "Child3" });
        RootNode.Children.Add(xx);
    }

    public TreeNodeViewModel RootNode
    {
        get { return _RootNode; }
        set
        {
            _RootNode = value;
            NotifyPropertyChanged(m => RootNode);
        }
    }

    public ObservableCollection<TreeNodeViewModel> RootNodeContainer
    {
        get { return _RootNodeContainer; }
        set
        {
            _RootNodeContainer = value;
            NotifyPropertyChanged(m => RootNodeContainer);
        }
    }
}

<TreeView Name="GoldTree" ItemsSource="{Binding RootNodeContainer}"
                  Background="#FFF0EDD1">
            <TreeView.Resources>
                <ResourceDictionary>
                    <Style TargetType="Button" x:Key="MyNodeStyle">
                        <Style.Triggers>
                            <DataTrigger
                                Binding="{Binding Path=IsValid,UpdateSourceTrigger=PropertyChanged}"
                                Value="False">
                                <Setter Property="Border.Background" Value="Red" />
                            </DataTrigger>
                            <DataTrigger
                                Binding="{Binding Path=IsValid,UpdateSourceTrigger=PropertyChanged}"
                                Value="True">
                                <Setter Property="Border.Background" Value="GreenYellow" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                    <Style TargetType="TreeViewItem">
                        <Setter Property="Margin" Value="0,0,0,0" />
                        <!-- Disable blue highlighting on selection-->
                        <Setter Property="Focusable" Value="false" />
                    </Style>
                    <HierarchicalDataTemplate DataType="{x:Type ViewModel:TreeNodeViewModel}"
                                              ItemsSource="{Binding Children}">
                        <Button Style="{StaticResource MyNodeStyle}" Content="{Binding Path=NodeText}" />
                    </HierarchicalDataTemplate>
                </ResourceDictionary>
            </TreeView.Resources>
        </TreeView>

您将获得以下信息:

此解决方案的限制是,在为每个节点设置 NodeText 属性时进行验证,因此您可能不知道该节点是否有子节点,所以我宁愿在 TreeViewModel 中创建一个方法来设置 @ 987654326@ 在某个时刻为所有节点标记。

【讨论】:

  • 是的。但我不确定如何将验证规则应用于我的树视图项。我知道风格触发器。但是如何将验证规则应用于我的树视图?
  • 您正在将验证规则应用于您的支持ViewModel View 的唯一职责是应用您在触发器中定义的样式并突出显示无效节点,并且一旦您定义,就会自动发生它。在ViewModel 方面进行更多思考。
  • 任何例子,因为我无法想象你所说的?
  • 在我的答案中添加了一些示例类。我正在使用 SimpleMVVM 库,但您可以手动实现通知。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-14
  • 2011-02-19
  • 2012-08-09
  • 1970-01-01
  • 1970-01-01
  • 2014-04-20
相关资源
最近更新 更多