0

I have a tree view like below.

  • R1
    • P1
      • Child 1
    • P2
      • Child 1
    • XX
      • Child 3

Say i want to display error message either in a) tree node or b) its container

based on the value my validation rule.

For exmaple if i apply the validation logic for this tree,

Validation rule >>> all parents whose value starts with "p" is valid

As a result , my "xx" node is in valid . I need to highlight this element and notify the user.

I am using MVVM in WPF. How do i do it. pls help me.

shahul hameed
  • 151
  • 2
  • 16

1 Answers1

2

I've done something similar in the past, here is what you can do:

  1. Create a ViewModel (e.g. NodeViewModel) that represents each of your tree nodes in case you don't already have one
  2. You can create and implement an interface in that NodeViewModel like IValidate that exposes a bool IsValid {get;set;} property. (Make sure you are implementing INotifyPropertyChanged in your ViewModel property)
  3. Create a Style for the control type you are using at your View for your nodes(e.g. Button) and create a Style trigger (lots of examples online) that sets the node background to red when the IsValid is false and apply it to your control:

                    <HierarchicalDataTemplate DataType="{x:Type ViewModel:NodeViewModel}" ItemsSource="{Binding Children}">
                        <Button Style="{StaticResource MyNodeStyle}" 
                                Content="{Binding Path=.}"
                    </HierarchicalDataTemplate>
    
  4. At your business logic layer, or container ViewModel create a method that validates all your nodes in the trees recursively and sets the IsValid property to all of them or define your business rule at the IsValid Get (make it read-only). When that validation happens your nodes will turn red automatically if they are not valid based on your rules.

Hope this helps, let me know if you have questions.

EDIT: Added some sample classes to illustrate the solution. My business rule was that any node that starts with 'p' is Valid (green) no matter if it is a parent or not, but you get the idea..

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>

Here is what you get:
Output

The limitation with this solution is that validation takes place when the NodeText property is set for each node so you may not know at that point whether the node has children, so I'd rather create a method in TreeViewModel to set the IsValid flag for all nodes at some point.

Adolfo Perez
  • 2,834
  • 4
  • 41
  • 61
  • Yeah. But I am not sure how to apply validation rules to my tree view item. I know about style trigger. But how to i apply validation rules to my tree view ? – shahul hameed Jul 29 '13 at 16:48
  • You are applying validation rules to your backing `ViewModel` the only responsibility of the `View` for this is to apply the styles you define in your trigger and highlight invalid nodes and that happens automatically once you define it. Think more on the `ViewModel` side. – Adolfo Perez Jul 29 '13 at 16:55
  • any example as i am not able to visualize what u said? – shahul hameed Jul 29 '13 at 17:06
  • Added some sample classes to my answer. I am using SimpleMVVM library but you can implement notifications manually. – Adolfo Perez Jul 29 '13 at 18:33