سؤال

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.

هل كانت مفيدة؟

المحلول

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top