How to disable some items in XAML for a WPF ListView

14,743

Solution 1

This is a fairly complex task, so you should consider doing this mostly in code rather than in XAML. If you were to do this entirely in code-behind, you could add a handler for the ListView.Loaded event, and then do all the logic of adding items and disabling certain items there. Admittedly, the ListView would not be data-bound, but in a special case like this you might be better off without the binding.

However, to show that this can be done in XAML, and using mark-up similar to yours, I have constructed the following example. My example uses Lists rather than XmlDataProvider, but the gist of it is exactly the same; you would just need to replace my code that builds Lists with your code that loads XML.

Here is my code-behind file:

public partial class Window2 : Window
{
    private List<Person> _persons = new List<Person>();

    public Window2()
    {
        InitializeComponent();

        _persons.Add(new Person("Joe"));
        _persons.Add(new Person("Fred"));
        _persons.Add(new Person("Jim"));
    }

    public List<Person> Persons
    {
        get { return _persons; }
    }

    public static List<Person> FilterList
    {
        get
        {
            return new List<Person>()
            {
                new Person("Joe"), 
                new Person("Jim")
            };
        }
    }
}

public class Person
{
    string _name;

    public Person(string name)
    {
        _name = name;
    }

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public override string ToString()
    {
        return _name;
    }
}   

This simply defines a couple lists, and the Person class definition, which holds a Name string.

Next, my XAML mark-up:

<Window x:Class="TestWpfApplication.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window2" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
    <local:PersonInListConverter x:Key="personInListConverter"/>
    <ObjectDataProvider x:Key="filterList" ObjectInstance="{x:Static local:Window2.FilterList}"/>
</Window.Resources>
<StackPanel>
    <ListView ItemsSource="{Binding Persons}"
              SelectionMode="Multiple"
              Name="lvwSourceFiles" Cursor="Hand" VerticalContentAlignment="Center">
        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="IsEnabled" 
                        Value="{Binding Name, Converter={StaticResource personInListConverter}, ConverterParameter={StaticResource filterList}}"/>
            </Style>
        </ListView.ItemContainerStyle>
    </ListView>
</StackPanel>

Here I bind the IsEnabled property of each ListViewItem to the Name property of the current Person. I then supply a Converter that will check to see if that Person's Name is on the List. The ConverterParameter points to the FilterList, which is the equivalent of your second XML file. Finally, here is the converter:

public class PersonInListConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string name = (string)value;
        List<Person> persons = (parameter as ObjectDataProvider).ObjectInstance as List<Person>;

        return persons.Exists(person => name.Equals(person.Name));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

And the end result:

alt text

Solution 2

My solution is not so simple, you need to create a dataTemplate for the items in the list. the data template should have a datatrigger, that means that you need to have some kind of property in the itemssource that tells you if the item exists in the other xml file.

so what i'm trying to say is that u need to read the xml file from code and generate your own custom class that will contain the name prop and the Exists property and bind it to the other file,

another solution that i thought of wile i was writing the answer is to bind the isenabled property of the item to a converter that will get the name and check the other file and return a boolean.

Share:
14,743
KevinDeus
Author by

KevinDeus

C# JQuery nunit WCF Subversion SharpSVN TFS RoombaSCI CreateOI Framework ChessMangler

Updated on June 11, 2022

Comments

  • KevinDeus
    KevinDeus almost 2 years

    OK, sorry for the overly broad question, but let's see what you guys suggest....

    I have a WPF ListView loaded by an XML file, using XAML (code below)

    I have a second XML file with items that match what is in my ListView. However, if there is not a match in the 2nd file, then I want that ListItem disabled.

    A simple example:

    My ListView has in it:

                       Joe
                       Fred
                       Jim  
    

    (because it was loaded with the first XML file)

    My second XML file has (essentially):

                      Joe
                      Jim
    

    I want the ListView to somehow consume this second file as well, resulting in "Fred" being disabled.

    I am assuming that it would be some sort of "Filter" I would apply somewhere in XAML.

    <ListView Name="lvwSourceFiles" 
              Margin="11,93,0,12" VerticalContentAlignment="Center" 
              HorizontalAlignment="Left" Width="306"
              Cursor="Hand" TabIndex="6" 
              ItemsSource="{Binding}"
              SelectionMode="Multiple"
              SelectionChanged="lvwSourceFiles_SelectionChanged" >
        <ListBox.DataContext>
            <XmlDataProvider x:Name="xmlSourceFiles" XPath="AssemblyUpdaterSource/sources/source/File" />
        </ListBox.DataContext>
        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <EventSetter Event="PreviewMouseRightButtonDown"
                             Handler="OnSourceListViewItemPreviewMouseRightButtonDown" />
            </Style>
        </ListView.ItemContainerStyle>
    </ListView>