Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to disable some items in XAML for a WPF ListView

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>
like image 279
KevinDeus Avatar asked Dec 04 '22 15:12

KevinDeus


1 Answers

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

like image 171
Charlie Avatar answered Dec 21 '22 05:12

Charlie