0
votes

My issue is that UI is not updated even when PropertyChanged is fired. XAML:

<ListBox Name="BookShelf" Visibility="Hidden" SelectedItem="{Binding SelectedItem}" Panel.ZIndex="1" Height="Auto" Grid.Column="3" Margin="8,50,0,0" HorizontalAlignment="Center"  ItemsSource="{Binding BookShelf}" Background="Transparent" Foreground="Transparent" BorderThickness="0" BorderBrush="#00000000">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel VerticalAlignment="Center" Orientation="Vertical">
                        <TextBlock FontSize="14" Margin="0,10,0,0" FontWeight="Bold" Foreground="Black" HorizontalAlignment="Center" Text="{Binding Path=DbId}" />
                        <TextBlock FontSize="16" FontWeight="Bold" Width="170" TextWrapping="Wrap" Foreground="Black" Margin="5" HorizontalAlignment="Center" Text="{Binding Path=DisplayName}" />
                        <Image HorizontalAlignment="Center" Source="{Binding Path=bookImage}" Width="200" Height="200" Margin="0,0,0,10" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

And:

<ComboBox Margin="8,15,0,0" Name="bookShelf_ComboBox" ItemsSource="{Binding BookShelf}"     SelectedItem="{Binding SelectedItem}" VerticalAlignment="Center" HorizontalAlignment="Center" DisplayMemberPath="DisplayName" Height="22" Width="140" Visibility="Visible" SelectionChanged="bookShelf_ComboBox_SelectionChanged"/>

Viewmodel:

public class BookShelfViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public event ShowContentInBrowser ShowDatabaseContent;

    public BookShelfViewModel(ShowContentInBrowser showMethod)
    {
        ShowDatabaseContent += showMethod;
    }
    private ObservableCollection<DbInfo> _BookShelf = new ObservableCollection<DbInfo>();
    public ObservableCollection<DbInfo> BookShelf
    {
        get
        {
            if (_BookShelf == null)
                _BookShelf = new ObservableCollection<DbInfo>();
            return _BookShelf;
        }
        set
        {
            if (value != _BookShelf)
                _BookShelf = value;
        }
    }
    private DbInfo _selectedItem { get; set; }
    public DbInfo SelectedItem
    {
        get
        {
            return _selectedItem;
        }
        set
        {
            if (_selectedItem != value)
            {
                _selectedItem = value;
                RaisePropertyChanged(new PropertyChangedEventArgs("SelectedItem"));
                if (_selectedItem == null)
                    return;
                if (_selectedItem.RelatedId != null)
                    ShowDatabaseContent(_selectedItem, _selectedItem.RelatedId);
                else
                    ShowDatabaseContent(_selectedItem, _selectedItem.RelatedId);

            }
        }
    }
    public void RaisePropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }
}

This code I'm using is for setting DataContext and SelectedItem:

await System.Windows.Application.Current.Dispatcher.BeginInvoke(
         DispatcherPriority.Background, new Action(
                () => this.BookShelf.SelectedItem = dbInfo
         )
      );

And DataContext:

await System.Windows.Application.Current.Dispatcher.BeginInvoke(
         DispatcherPriority.Background, new Action(
                () => this.BookShelf.DataContext = bookShelfViewModel
         )
       );

I'm very new for this MVVM design and as far as I have can tell from articles I have read, I cant find what's wrong. I'm guessing that using Dispatcher is not necessary but I don't think it matters in this case... ListBox does show my objects but updating SelectedItem is the issue here...

UPDATE:

Heres my code for DbInfo:

public class DbInfo
{
    public int RelatedId { get; set; }
    public string DbId { get; set; }
    public TBase3.Article currentArticle { get; set; }
    public string LinkId { get; set; }
    public bool IsArticle { get; set; }
    public string folder { get; set; }
    public bool IsNamedArticle { get; set; }
    public int currentBlockIndex { get; set; }
    public int currentBlockCount { get; set; }
    public string DisplayName { get; set; }
    public int VScrollPos { get; set; }
    public int THTextVersion { get; set; }
    public bool isHtmlToc { get; set; }
    public ImageSource bookImage { get; set; }
}

Reminder that when ever I set new value for ViewModel -> SelectedItem and It goes to PropertyChanged(this, e); line. It does not Selected that DbInfo as Selected in ListBox.

Update2:

I got right side of my window a list of books, like a Book Shelf many books in it. It shows all book with scroll. Book which is selected its content is being shown in window. But If for reason I want to change to another book from code-behind, it updates it content to webbrowser but not update ListBox that certain book as SelectedItem

ANSWER: Okay I found the answer right now. Code which set BookShelf.SelectedItem = dbInfo should be bookShelfViewModel.SelectedItem = bookShelfViewModel.BookShelf.First(x => x.DbId == dbInfo.DbIf);

3
Have you check the binding mode on XAML? You will want to use the TwoWay mode if you are updating both the view model from the view and having the view updated from the view model - aggietech
@aggietech You mean the SelectedItem binding? That property binds two-way by default. - Clemens
bookShelf_ComboBox_SelectionChanged is not firing anything right? You are trying to get the "SelectionChanged" event to fire a command in your view model? - aggietech
Exactly how do you define the DataContext? If you are certain the PropertyChanged event is raised, but the UI is not updated, I would think that the DataContext is not set correctly. - sondergard
@Jon Koivula someone asked the same question as you (at amost the same time), I gave an example solution, check it out and apply it to your model if you wish. stackoverflow.com/questions/25774160/… - Chubosaurus Software

3 Answers

2
votes
await System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => this.BookShelf.DataContext = bookShelfViewModel));

That does not look good, where do you do this? I would reccomend the use of Galasoft MVVM Light and the usage of a ViewModelLocator, for setting your DataContext(avaliable through nuget).It provides you with a ViewModelBase, with all your propertychanged needs and the works, which you may extend to suit your neeeds. It sounds like the DataContext, may be the actual problem if PropertyChanged isn't even raised.

EDIT:

As pointed out by Clemens the Mode=TwoWay in the binding is not needed here, as it is the default for the SelectedItem property anyway, I'll just leave it as an example....

<ComboBox Margin="8,15,0,0" Name="bookShelf_ComboBox" ItemsSource="{Binding BookShelf}"  SelectedItem="{Binding SelectedItem, Mode=TwoWay}" VerticalAlignment="Center" HorizontalAlignment="Center" DisplayMemberPath="DisplayName" Height="22" Width="140" Visibility="Visible">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <command:EventToCommand Command="{Binding SelectedItemChangedCommand}" PassEventArgsToCommand="True"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ComboBox>

I noticed your SelectionChanged="bookShelf_ComboBox_SelectionChanged" in the code, this is "nasty", use event to command instead, because you are tying your viewmodel to your views cbox. The code above will execute a command with arguments included, add the following to your viewmodel.

public ICommand SelectedItemChangedCommand{ get; set;}

// somewhere in your code..
SelectedItemChangedCommand = new RelayCommand<SelectedItemChangedCommand>(OnSelectedItemChanged);

protected void OnSelectedItemChanged(SelectedItemChangedCommand e)
{
   // Do your magic here! Or why not just call this method from the setter of your bound property?
}

The WPF Cheat Sheet is a nice compact list of all types of bindings, which is very handy, I used to have it both at wall and home, when I was learning WPF :)

Hope it helps

Cheers

Stian

0
votes

what you mean by the UI is not updated? you set a new ItemsSource and dont see any Changes?

if that is the case change your Property to

public ObservableCollection<DbInfo> BookShelf
{
    get
    {
        if (_BookShelf == null)
            _BookShelf = new ObservableCollection<DbInfo>();
        return _BookShelf;
    }
    set
    {
        if (value != _BookShelf)
        {   _BookShelf = value;
            RaisePropertyChanged(new PropertyChangedEventArgs("BookShelf"));
        }
    }

btw i use ObservableCollection in another way. i just initialize it once and use Clear, Add, Remove.

If the ItemsSource is not your Problem pls post the Code for DbInfo, and write something more to your "UI is not updated" problem

0
votes

Okay I found the answer right now. Code which set BookShelf.SelectedItem = dbInfo should be bookShelfViewModel.SelectedItem = bookShelfViewModel.BookShelf.First(x => x.DbId == dbInfo.DbIf);