Retrocoder
Retrocoder

Reputation: 4713

Setting a treeview items text colour in realtime based on a ViewModel property

In my WPF application I am using the MVVM pattern. My view has a treeview which I bind an observableCollection of objects as defined below. What I want to do is to change the colour of a tree item name when the bound object sets it’s dirty property to true. I can get it to set the colour when I first populate the tree but then it doesn’t reflect the changes when the property changes between false and true.

public class HierarchicalItem
{
    private readonly ObservableCollection<HierarchicalItem> _children = new ObservableCollection<HierarchicalItem>();

    public ViewModelBase ViewModel { get; set; }

    public string Name
    {
        get { return ViewModel.ViewModelName; }
    }

    public ICollection<HierarchicalItem> Children
    {
        get { return _children; }
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            if (_isSelected)
                EventSystem.Publish(new SelectedViewModelMessage { SelectedViewModel = ViewModel });
        }
    }

    public bool IsDirty
    {
        get { return ViewModel.IsDirty; }
    }
}

This is the treeview xaml:

    <TreeView Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=Views}">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="True"/>
                <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWayToSource}" />
            </Style>
        </TreeView.ItemContainerStyle>

        <TreeView.Resources>

            <HierarchicalDataTemplate DataType="{x:Type local:HierarchicalItem}" ItemsSource="{Binding Children}">
                <TextBlock Text="{Binding Name}">

                    <TextBlock.Style>
                        <Style TargetType="TextBlock">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsDirty}" Value="True">
                                    <Setter Property="Foreground" Value="Red" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBlock.Style>

                </TextBlock>
            </HierarchicalDataTemplate>

        </TreeView.Resources>
    </TreeView>

Here is the collection that gets bound to the tree:

    private readonly ObservableCollection<HierarchicalItem> _views = new ObservableCollection<HierarchicalItem>();
    public ObservableCollection<HierarchicalItem> Views
    {
        get { return _views; }
    }

The ViewModels that are referenced in the HierarchicalItem collection all derive from a base class that exposes the “IsDirty” property. This is definantly changing state so I’m not sure if I’ve made a coding mistake or if what I want to achieve can’t be done this way. The classes all use the “INotifyPropertyChanged” interface. Here is the “IsDirty” property in from the ViewModel base class:

public class ViewModelBase : ValidatableModel
{
    #region Properties

    private bool _isDirty;
    public bool IsDirty
    {
        get { return _isDirty; }
        protected set
        {
            _isDirty = value;
            OnPropertyChanged("IsDirty");
        }
    }

 .
 .
 .
 Etc

Upvotes: 0

Views: 1063

Answers (1)

sacha barber
sacha barber

Reputation: 2333

It's because your HierarchicalItem (the one you are having issues with) does not use a full INPC approach for its IsDirty property. The viewmodel does, but that is not enough, as the DataTemplate will be using the IsDirty property of the HierarchicalItem, so that needs to be full INPC property too

Changed that to this and it should be ok.

private bool _isDirty;
public bool IsDirty
{
    get { return _isDirty; }
    protected set
    {
        _isDirty = value;
        OnPropertyChanged("IsDirty");
    }
}

Though for your use case you will need to figure out some way to fire that. Or another thing you could try would be to change the binding in HierarchicalItem DataTemplate to this

<DataTrigger Binding="{Binding ViewModel.IsDirty}" Value="True">

Upvotes: 1

Related Questions