Powered by MSDN

US - English
NEW! Silverlight 5 is available Learn More

How do I disable a TreeViewItem in TreeView? RSS

12 replies

Last post Jun 29, 2011 08:12 PM by nestorarturo

(0)
  • peteot

    peteot

    Member

    19 Points

    22 Posts

    How do I disable a TreeViewItem in TreeView?

    Feb 09, 2009 04:01 PM | LINK

    If I'm databinding to a tree of custom objects, how do I then access an individual TreeViewItem to set IsEnabled? Ideally, I want to do this via databinding. I'm wondering if using ItemContainerStyle would work? I think I'm getting a little confused at the relationship between ItemContainerStyle, ItemTemplate, etc. Obviously the TreeViewItem is created for us when we databind to plain old objects, but how do we set its properties, in particular with binding?
  • JustinAngel

    JustinAngel

    Contributor

    4455 Points

    606 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Feb 09, 2009 08:33 PM | LINK

    Hi,

    You've hit a Silverlight roadblock.

    Unfortunately for ItemsControl (TreeView)  there's no way to Bind the properties of the generated Container (TreeViewItem) to the Item (your BL class).

    It's a known limitation and you'll have to use code to go around it. So XAML based DataBinding is out for now. Though you can still set up bindings from code-behind.

    Another way of going about doing that is creating your own TreeView that overrides the PrepareContainerForItemOverride method.
    That's the place where in ItemsControls you can do all sorts of wacky things for Containers.

        public class IsEnabledTreeView : TreeView

        {

            protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

            {

                base.PrepareContainerForItemOverride(element, item);

     

                if (element is IsEnabledTreeViewItem && item is SilverlightControl53.myItem)

                {

                    IsEnabledTreeViewItem curContainer = (IsEnabledTreeViewItem) element;

                    SilverlightControl53.myItem curItem = (SilverlightControl53.myItem)item;

     

                    curContainer.IsEnabled = curItem.IsEnabled;

                }

            }

        }

     After you do that, you'll have to do some more "ItemsControl" plumbing. You'll need to override the GetContainer and IsItemItsOwnContainer method, and repeat the same code for a Custom TreeViewItem.

    Here's the full code:

        public class IsEnabledTreeView : TreeView

        {

            protected override DependencyObject GetContainerForItemOverride()

            {

                return new IsEnabledTreeViewItem();

            }

     

            protected override bool IsItemItsOwnContainerOverride(object item)

            {

                return item is IsEnabledTreeViewItem;

            }

     

            protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

            {

                base.PrepareContainerForItemOverride(element, item);

     

                if (element is IsEnabledTreeViewItem && item is SilverlightControl53.myItem)

                {

                    IsEnabledTreeViewItem curContainer = (IsEnabledTreeViewItem) element;

                    SilverlightControl53.myItem curItem = (SilverlightControl53.myItem)item;

     

                    curContainer.IsEnabled = curItem.IsEnabled;

                }

            }

        }

     

        public class IsEnabledTreeViewItem : TreeViewItem

        {

            protected override DependencyObject GetContainerForItemOverride()

            {

                return new IsEnabledTreeViewItem();

            }

     

            protected override bool IsItemItsOwnContainerOverride(object item)

            {

                return item is IsEnabledTreeViewItem;

            }

     

            protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

            {

                base.PrepareContainerForItemOverride(element, item);

     

                if (element is IsEnabledTreeViewItem && item is SilverlightControl53.myItem)

                {

                    IsEnabledTreeViewItem curContainer = (IsEnabledTreeViewItem)element;

                    SilverlightControl53.myItem curItem = (SilverlightControl53.myItem)item;

     

                    curContainer.IsEnabled = curItem.IsEnabled;

                }

            }

        }

    Usage is actually pretty simple:

    <UserControl x:Class="SL_RTM_VS.SilverlightControl53"

       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"

       xmlns:SL_RTM_VS="clr-namespace:SL_RTM_VS"

       Width="400" Height="300">

        <UserControl.Resources>

            <controls:HierarchicalDataTemplate x:Key="myHierarchicalTemplate" ItemsSource="{Binding Items}">

                <TextBlock Text="{Binding myString}" />

            </controls:HierarchicalDataTemplate>

        </UserControl.Resources>

        <Grid x:Name="LayoutRoot" Background="White">

            <SL_RTM_VS:IsEnabledTreeView x:Name="trv" ItemTemplate="{StaticResource myHierarchicalTemplate}" />

        </Grid>

    </UserControl>

    And in our code behind we'll set the datasource:

                trv.ItemsSource = new myItem[]

                                      {

                                          new myItem(true, "Hello",

                                              new myItem(true, "World"),

                                              new myItem(false, "Foo"),

                                              new myItem(false, "Bar")),

                                          new myItem(true, "Moo",

                                              new myItem(false, "Boo",

                                                new myItem(false, "Goo"))),

                                      };

            public class myItem

            {

                public myItem(string myString)

                {

                    this.myString = myString;

                }

     

                public myItem(bool IsEnabled, string myString, params myItem[] myItems)

                    : this(myString)

                {

                    ObservableCollection<myItem> itemsObservableCollection = new ObservableCollection<myItem>();

                    foreach (var item in myItems)

                        itemsObservableCollection.Add(item);

                    Items = itemsObservableCollection;

                    this.IsEnabled = IsEnabled;

                }

     

                public string myString { get; set; }

                public ObservableCollection<myItem> Items { get; set; }

                public bool IsEnabled { get; set; }

            }

    And here's what we got:

     

     

    There's a few places where this solution can be improved on (using a DataBinding, exposing TreeView.IsEnabledPath, not having a hard dependency on myItem) but for you problem we might be in the clear.

    Sincerely,

    --
    Justin Angel,
    Twitter @ http://twitter.com/JustinAngel
    Blog @ http://JustinAngel.net
  • peteot

    peteot

    Member

    19 Points

    22 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Feb 09, 2009 09:03 PM | LINK

    Justin, Thanks so much for the response. I had just started down the road of overriding PrepareContainerForItemOverride and it's working, but I didn't realize I need to do this for TreeViewItem as well, and I didn't realize I needed to override some additional methods that you've mentioned. This is great, thanks a lot! It appears that the ItemContainerGenerator for each item comes from its parent TreeViewItem, or the TreeView itself if it's a root node, correct? I was running into some other problems and came to this conclusion. I assume this is why we need to override these methods in TreeViewItem as well? Thanks again! Pete
  • peteot

    peteot

    Member

    19 Points

    22 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Feb 09, 2009 09:30 PM | LINK

    One other question ... Do you have any idea if it's possible for individual treeviewitems to be expandable, but not selectable? It seems like IsEnabled=False disables user expansion and selection. This probably makes sense in most scenarios... I'm assuming I'll have to retemplate if I need this.
  • JustinAngel

    JustinAngel

    Contributor

    4455 Points

    606 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Feb 09, 2009 09:37 PM | LINK

    no problem.

    ItemsControl is a pretty interesting topic and there's been a slough of blog posts about it in the last month:
    Part 1: Inheriting from ItemsControl
    Part 2: Building the ItemContainerGenerator
    Refactoring the CoolMenu Control – Part 3: CoolMenu Behaviors
    How to inherit from ItemsControl and create a UniformGrid with containers
    Refactoring Focus Panel: ItemsControl

    The TreeView ItemContainerGenerator is hierarchical and works exactly like you described. TreeView.ItemContainerGenerator is for 1st level TreeViewItems and TreeViewItem.ItemContainerGenerator are for level (i+1) where i is the parent TreeViewItem level.

    You do need to retemplate a control to change the way non-selectable items work.

     

    --
    Justin Angel,
    Twitter @ http://twitter.com/JustinAngel
    Blog @ http://JustinAngel.net
  • Ciaran Murphy

    Ciaran Murphy

    Member

    189 Points

    82 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Jun 23, 2009 04:30 PM | LINK

    Another neat method here:

    http://blogs.msdn.com/delay/archive/2009/05/07/one-more-platform-difference-more-or-less-tamed-settervaluebindinghelper-makes-silverlight-setters-better.aspx

     Just set the style in the ItemContainer style - any property can be bound. Neat :-)

     

  • t-lo

    t-lo

    Member

    6 Points

    3 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Jul 28, 2009 01:18 PM | LINK

     Nice link there Ciaran. Using it right now, but I've encountered one issue. I'm using a HierarchicalDataTemplate which works just fine otherwise. Updates dynamically when publishing OnPropertyChanged and all, but when setting a treeviewitem with the IsEnabled to false (and sending the propertychanged event), nothing changes in the list. Also tried setting the IsSelected property to false, no change what so ever.

     I tried setting the IsEnabled to false from the start and then the root nodes are all disabled, but I can't seem to get it working while running. I guess launching OnPropertyChanged only looks for changes in the data source and not the treeviewitem, so my question is what's the way to make the treeview control refresh it's treeviewitems and how could I do this programmatically?

     

    Tobias Lolax

     

    EDIT:

    To add a bit more to this post: I'm using a PresenterModel that subscribes to the SelectedItemChanged event, and when I manually, in the event handler, added ((TreeView)sender).GetSelectedContainer().IsEnabled = false; it currectly disabled the selected item. The problem is just that I would like this to work dynamically by reading from the binding.

     The XAML looks as the following where I set the binding: 

            <controls:TreeView x:Name="treeView" Margin="2,5,0,0" d:LayoutOverrides="Width, Height" ItemsSource="{Binding Path=ThreadableLocations, Mode=OneWay}" ItemTemplate="{StaticResource LocationTemplate}" BorderThickness="1,20,1,1" Background="{x:Null}" BorderBrush="{x:Null}">
    <controls:TreeView.ItemContainerStyle>
    <Style TargetType="controls:TreeViewItem">
    <Setter Property="helper:SetterValueBindingHelper.PropertyBinding">
    <Setter.Value>
    <helper:SetterValueBindingHelper Property="IsEnabled" Binding="{Binding Path=IsEnabled}" />
    </Setter.Value>
    </Setter>
    <Setter Property="helper:SetterValueBindingHelper.PropertyBinding">
    <Setter.Value>
    <helper:SetterValueBindingHelper Property="IsSelected" Binding="{Binding Path=IsSelected}" />
    </Setter.Value>
    </Setter>
    </Style>
    </controls:TreeView.ItemContainerStyle>
    </controls:TreeView>
     
  • Ciaran Murphy

    Ciaran Murphy

    Member

    189 Points

    82 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Jul 28, 2009 01:56 PM | LINK

    Hmmm strange... it works quite reliably for me. Although I'm only using it to set the IsExpanded and IsSelected Properties of my TreeItems(havn't tried the Enabled property). It works at run time for me. I can set the selected property on my object and it updates on the tree no problem.
    You say that you're using a  HierarchicalDataTemplate... well that's normal in a treeview. But you shouldn't be setting these properties on the DataTemplate at all you should set the on the ItemContainer. The ItemContainer is the entity that hosts whatever you have defined in your HierarchicalDataTemplate... You could enable/disable the controls inside your template all you like and the TreeItem would still be enabled.

    <Grid x:Name="LayoutRoot">

    <controls:TreeView
    ItemsSource="{Binding Path=myItemsSource}"
    ItemTemplate="{StaticResource myHierarchicalDataTemplate}"
    ItemContainerStyle="{StaticResource myItemContainerStyle}"             
    Style="{StaticResource myTreeViewStyle}"/>

    </Grid>


    The myItemContainerStyle is the important part here. In Blend (or whatever) make a copy of the TreeViewItemStyle

    <Style x:Key="myItemContainerStyle" TargetType="controls:TreeViewItem">     
    <Setter Property="IsExpanded" Value="False" />     
    <Setter Property="myNameSpace:SetterValueBindingHelper.PropertyBinding">           
    <Setter.Value>
    <myNameSpace:SetterValueBindingHelper  Property="IsSelected" Binding="{Binding Mode=TwoWay, Path=IsSelected}"/>
    </Setter.Value>       
    </Setter>
    </Style>


    I can't send you a sample right now (busy and can't share company code), but if you're having difficulties I could get you a small work sample by the weekend.

  • Ciaran Murphy

    Ciaran Murphy

    Member

    189 Points

    82 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Jul 28, 2009 02:17 PM | LINK

    Oh ok you are using the ItemContainerStyle... saw it after your edit.

    Could you do an experiment for me... obviously your setting the sytles inline rather than as a StaticResource. Try as a StaticResource and add in a Loaded event handler in the code-behind for some of the framework elements in your DataTemplate... just to check your DataContext... maybe its not as you expect.

  • t-lo

    t-lo

    Member

    6 Points

    3 Posts

    Re: How do I disable a TreeViewItem in TreeView?

    Jul 28, 2009 02:52 PM | LINK

     Tried making it a static resouce instead, to no avail. And then I tried just changing the Property to work against IsExpanded instead of IsEnabled and voilá, as soon as the new objects are fetched from the db and added to the child collection (which I have as datatemplate itemsource), the new items appear and all levels expanded. So the property part seems to be working and being read from the bound objects properties.

     So this basically means that it reads the object property when adding it to the treeview and sets the container property to what it should be. But it doesn't seem to update when the data source's property IsEnabled changes. AND now it just struck me that I haven't implemented the OnPopertyChanged for the IsEnabled propery :) doh!

    Just tested and now it's all working. Guess it's always have been, just me being to tired to see the obvious.

    Thanks for replying and trying to help out tho', great to get feedback that fast!