简体   繁体   中英

WPF ListBox property binding not updating

I have the following setup:

XAML:

<ListBox x:Name="MyList" ItemsSource="{Binding MyItems}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Image Height="20" Width="20" Visibility="{Binding HasInformation, Converter={StaticResource VC}, ConverterParameter=True}" Source="/path/to/information.png" />
                <TextBlock Text="{Binding Name}" VerticalAlignment="Center" Padding="5,0" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Note: The ConverterParameter being passed in simply controls whether the visibility is "Collapsed" ( False ), or "Hidden" ( True ), so in this case, I want the visibility to be Hidden .

ViewModel Snippet:

private ObservableCollection<IItem> _MyItems;
public ObservableCollection<IItem> MyItems
{
    get
    {
        return _MyItems;
    }
    set
    {
        NotifyPropertyChanged(ref _MyItems, value, "MyItems");
    }
}

private IItem _SelectedItem;
public IItem SelectedItem
{
    get
    {
        return _SelectedItem;
    }
    set
    {
        NotifyPropertyChanged(ref _SelectedItem, value, "SelectedItem");
    }
}

IItem:

public interface IItem
{
    string Name { get; }
    bool HasInformation { get; set; }
}

I populate an implementation of a list of IItem from a database into the list, and the information icon appears appropriately if HasInformation is true. This all works correctly.

However, if I set HasInformation by hand, the view does not update. I have tried:

In the ViewModel:

OnPropertyChanged("MyItems");
MyItems[MyItems.IndexOf(SelectedItem)].HasInformation = true;
// Note that "SelectedItem" is persisted correctly, and always
// points to the selected item that we want to update.

In the code behind:

MyList.GetBindingExpression(ItemsControl.ItemsSourceProperty).UpdateTarget();

All of these fire the getter of the MyItems property, but the view never updates and the icon never displays. I have ensured that the HasInformation property of the item that I updated does, in fact, remain true . I've attached to the PropertyChanged event to ensure that it's firing a property change for "MyItems" (it is, this also fires the getter), and I've even ensured that it's calling the value converter with the correct value for the HasInformation property (it is!), so what am I missing? Is there something weird with image showing/hiding or visibility value conversion that I'm not handling correctly?

ObservableCollection only notifies the collection changes not the changes in each of the item. In order to achieve your goal one of options is to change the IItem from interface to a class which implements INotifyPropertyChanged interface (or implement it in the IItem concrete type), and hook it with the ViewModel's PropertyChanged delegate (remember to unsubscribe it). See some of my code below.

ViewModel

public class MyViewModel: INotifyPropertyChanged
{
    private ObservableCollection<Item> _MyItems;
    public ObservableCollection<Item> MyItems
    {
        get
        {
            return _MyItems;
        }
        set
        {
            if (_MyItems != null)
            {
                foreach (var item in _MyItems)
                {
                    item.PropertyChanged -= PropertyChanged;
                }
            }

            if (value != null)
            {
                foreach (var item in value)
                {
                    item.PropertyChanged += PropertyChanged;
                }
            }

            OnPropertyChanged();
        }
    }

    private Item _SelectedItem;
    public Item SelectedItem
    {
        get
        {
            return _SelectedItem;
        }
        set
        {
            _SelectedItem = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Item

public class Item : INotifyPropertyChanged
{
    private string _name;
    private bool _hasInformation;

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

    public bool HasInformation
    {
        get { return _hasInformation; }
        set
        {
            _hasInformation = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM