简体   繁体   中英

WPF MVVM Update Listbox

Issue


I am trying to update values in my ListBox when a user select certain data. For some reason the ListBox does not update real time and i have to close the application down and re-open for the data to appear.

I have changed the pattern to MVVM as people have said this is easier to work with.

Code


On my form I set the DataContext:

public Live()
{
    LiveMainViewModel graphUpdater = new LiveMainViewModel();
    this.DataContext = graphUpdater;
}

In my ViewModel Constructor I call a refresh:

public LiveMainViewModel()
{
    RefreshListBox();
}

I set a Property with OnPropertyChanged event attached:

public ObservableCollection<ITimeLineDataItem> SlideDataItems { 
    get { return _slideDataItems; }
    set
    {
        _slideDataItems = value;
        OnPropertyChanged("SlideDataItems");
    }
}
private ObservableCollection<ITimeLineDataItem> _slideDataItems;

In my refresh method I add the new items to the list:

public void RefreshListbox()
{
    _slideDataItems = new ObservableCollection<ITimeLineDataItem>();

    foreach (podiaPublish.Marker pMarker in Global.gChapter.MarkerList)
    {
        if (pContent.Markup.Contains(".png"))
        {
             var brush = new ImageBrush(bitmapSource);

             var lb1 = new TempDataType()
             {
                 Name = pContent.Markup,
                 BackgroundImage = brush
             };

             _slideDataItems.Add(lb1);
         }
    }
}

Summary


So when I add the items real time the ListBox will not update with the data but when I reload the application the data will appear.

Question


How can I get the ListBox to update with the data that the user has added?

Edit


Output Window:

System.Windows.Data Warning: 56 : Created BindingExpression (hash=10555762) for Binding (hash=14988671)
System.Windows.Data Warning: 58 :   Path: 'SlideDataItems'
System.Windows.Data Warning: 60 : BindingExpression (hash=10555762): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=10555762): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=10555762): Attach     to System.Windows.Controls.ListBox.ItemsSource (hash=30133081)
System.Windows.Data Warning: 67 : BindingExpression (hash=10555762): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=10555762): Found data context element: ListBox (hash=30133081) (OK)
System.Windows.Data Warning: 71 : BindingExpression (hash=10555762): DataContext is null
System.Windows.Data Warning: 65 : BindingExpression (hash=10555762): Resolve source deferred

How I Bind:

<ListBox x:Name="ListSrc" Background="#ececec" ItemsSource="{Binding SlideDataItems}" dd:DragDrop.IsDragSource="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" BorderBrush="Transparent" BorderThickness="0">

My Custom Class:

public interface ITimeLineDataItem
{
    TimeSpan? StartTime { get; set; }
    TimeSpan? EndTime { get; set; }
    Boolean TimelineViewExpanded { get; set; }
}

It's because you are changed binded object to another one but do not notify your binding about that. In this case you can replace your code with next one:

public void RefreshListbox()
{
    var slideDataItems = new ObservableCollection<ITimeLineDataItem>();

    foreach (podiaPublish.Marker pMarker in Global.gChapter.MarkerList)
    {
        if (pContent.Markup.Contains(".png"))
        {
             var brush = new ImageBrush(bitmapSource);

             var lb1 = new TempDataType()
             {
                 Name = pContent.Markup,
                 BackgroundImage = brush
             };

             slideDataItems.Add(lb1);
         }
    }

    SlideDataItems = slideDataItems;
}

In this case you change object with notification from the property. Data target (ListBox) will be triggered and update its presentation for new data source.

The other way is to change existed data source by switching

_slideDataItems = new ObservableCollection<ITimeLineDataItem>();

to the

_slideDataItems.Clear();

and initialize field on its declaration:

private ObservableCollection<ITimeLineDataItem> _slideDataItems = new ObservableCollection<ITimeLineDataItem>();

Now you will have one data source object always and can update it by adding new elements, deleting elements or clear all collection.

Try in the xhtml:

ItemsSource="{Binding SlideDataItems, UpdateSourceTrigger=PropertyChanged}"

and maybe also add Mode=TwoWay

Edit:

why are you Refreshing the ListBox and createing a new ObservableCollection?

Try to trigger the OnPropertyChanged("SlideDataItems"); in the Method RefreshListbox() at the end.

I think you have implemented wrong the ObservableCollection. The properties of ITimeLineDataItem, should implement the INotifyPropertyChanged and not the collection.

public class MyCollection : ObservableCollection<ITimeLineDataItem>
{
}

public class ITimeLineDataItem: INotifyPropertyChanged
{
    private string _aProperty;
    public string  AProperty
    {
        get { return _aProperty; }
        set
        {
            if (_aProperty!= value)
            {
                _aProperty= value;
                OnPropertyChanged("AProperty");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Then in your view model (which will be also be you data context) you will have:

MyCollection<ITimeLineDataItem> SlideDataItems {get; set;}

and

SlideDataItems  = MyCollection<ITimeLineDataItem>();

Finally you need to set, in your xaml

ItemsSource="{Binding SlideDataItems}"

Create an ObservableCollection once, then simply clear it in the Refresh method.

public ObservableCollection<ITimeLineDataItem> SlideDataItems { 
    get { return _slideDataItems; }
    set
    {
        _slideDataItems = value;
        OnPropertyChanged("SlideDataItems");
    }
}
private ObservableCollection<ITimeLineDataItem> _slideDataItems = new ObservableCollection<ITimeLineDataItem>();

In the Refresh method, clean the ObservableCollection, but do not recreate it.

public void RefreshListbox()
{
    SlideDataItems.Clear();

    foreach (podiaPublish.Marker pMarker in Global.gChapter.MarkerList)
    {
        if (pContent.Markup.Contains(".png"))
        {
             var brush = new ImageBrush(bitmapSource);

             var lb1 = new TempDataType()
             {
                 Name = pContent.Markup,
                 BackgroundImage = brush
             };

             _slideDataItems.Add(lb1);
         }
    }
}

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