0

I'm struggling with update issue. I have tab control with the listbox binded to an Observable Collection

ListBox HorizontalAlignment="Center" Height="450" VerticalAlignment="Top" Width="250"
     x:Name="LbxMenu" Background="{x:Null}" BorderBrush="{x:Null}" 
     ItemsSource="{Binding TestListsNames}" FontFamily="Segoe UI Semilight" FontSize="18"/>

view model:

    private ObservableCollection<string> _testListsName;

    public ObservableCollection<string> TestListsNames
    {
        get { return _testListsName; }
        set{ _testListsName = value; }
   }

After inserting entity to database there is an event which invokes TestListInitialize method in my ViewModel which should refresh collection and it works as I can see it in debugger. But listbox doesn't refresh and I have to restart application to see changes. It worked great when it was in separate window but when I changed ui to tab control it doesn't.

Update function:

private void TestListNamesInitialize()
    {
        TestListsNames = db.GetTestListNamesFromDatabase();
        if (TestListsNames.Count != 0) CanLoad = true;           

    }

Initial Window:

<Controls:MetroWindow
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="Test.View.InitialWindow"
    xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
    xmlns:tabdata="clr-namespace:Test.View.TabItems"

    Title="Testownik" Height="600" Width="900" ShowTitleBar="True" ResizeMode="NoResize" Icon="../GraphicResources/Icon.ico">


<Controls:MetroWindow.RightWindowCommands>
    <Controls:WindowCommands>
        <Button Content="settings" />
        <Button>
            <StackPanel Orientation="Horizontal">
                <TextBlock Margin="4 0 0 0"
               VerticalAlignment="Center"
               Text="about" />
            </StackPanel>
        </Button>
    </Controls:WindowCommands>
</Controls:MetroWindow.RightWindowCommands>
<Controls:MetroAnimatedTabControl x:Name ="MainTabControl">
    <TabItem Header="Learn" Width="280">
        <tabdata:LearnTabItem/>
    </TabItem>
    <TabItem Header="Database" Width="280">
        <tabdata:DatabaseTabItem/>
    </TabItem>
    <TabItem Header="Statistics" Width="299">
        <tabdata:StatisticsTabItem/>
    </TabItem>
</Controls:MetroAnimatedTabControl>

Code behind:

    public partial class InitialWindow : MetroWindow
{
    InitialWindowViewModel viewModel=new InitialWindowViewModel();
    public InitialWindow()
    {
       InitializeComponent();
       DataContext = viewModel;
    }
}

}

DatabaseTabItem:

<UserControl x:Class="Test.View.TabItems.DatabaseTabItem"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
         xmlns:tabData="clr-namespace:Test.View.TabItems"
         Height="500" Width="900" Background="White" BorderBrush="Transparent">
<UserControl.Resources>


</UserControl.Resources>
<Grid>

    <Controls:MetroAnimatedTabControl x:Name ="DatabaseTabControl" Grid.Column="0" TabStripPlacement="Left" >
        <TabItem Header="Choose" Width="250" >
            <tabData:ChooseFromDbTabItem/>
        </TabItem>
        <TabItem Header="Add" Width="250">
            <tabData:AddToDbTabItem/>
        </TabItem>
        <TabItem Header="Remove" Width="250">
            <tabData:DeleteFromDbTabItem/>
        </TabItem>
    </Controls:MetroAnimatedTabControl>
</Grid>

code behind:

    DatabaseViewModel vm = new DatabaseViewModel();
    public DatabaseTabItem()
    {
        InitializeComponent();
        DataContext = vm;
    }

}

ChooseFromDbTabItem:

<UserControl x:Class="Test.View.TabItems.ChooseFromDbTabItem"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:Test.View.TabItems"
         xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"

         mc:Ignorable="d" 
         d:DesignHeight="500" d:DesignWidth="650" Background="White" BorderBrush="Transparent">

<Grid>
    <ListBox HorizontalAlignment="Center" Height="450" VerticalAlignment="Top" Width="250"
     x:Name="LbxMenu" Background="{x:Null}" BorderBrush="{x:Null}" 
     ItemsSource="{Binding TestListsNames}" FontFamily="Segoe UI Semilight" FontSize="18"/>
</Grid>

code behind:

  public partial class ChooseFromDbTabItem : UserControl
{
    public ChooseFromDbTabItem()
    {
        InitializeComponent();
    }
}
derb
  • 31
  • 7
  • 1
    How are you updating the list? Can you provide that code? That is, the TestListInitialize method. I suspect that you are using the property setter, which does NOT raise any PropertyChanged event. – lenkan Jan 03 '16 at 11:03
  • Database: `public void ImportToDatabase(string testName) { _testName = testName; DocumentProcessor processor = new DocumentProcessor(); processor.ProcessDocFile(); OnDatabaseUpdated(new EventArgs()); }` ViewModel: contructor: `public DatabaseViewModel() { db = new DatabaseOperations(); ..... db.DatabaseUpdated += (sender, args) => TestListNamesInitialize(); } ` – derb Jan 03 '16 at 11:11
  • please update your question with that code. It is hard to read code in the comment section. – lenkan Jan 03 '16 at 11:12
  • function: `private void TestListNamesInitialize() { TestListsNames = db.GetTestListNamesFromDatabase(); if (TestListsNames.Count != 0) CanLoad = true; } ` – derb Jan 03 '16 at 11:13

2 Answers2

1

You are not raising the PropertyChanged event when replacing the list using the property setter. Generelly, try to make collection properties readonly to reduce the risk for these sort of errors. Instead, clear the list and repopulate it. This will make sure that the view is notified about any changes.

public class ViewModel
{
    private readonly ObservableCollection<string> _testListsName;

    public ObservableCollection<string> TestListsNames
    {
        get { return _testListsName; }
    }

    private void TestListNamesInitialize()
    {
        _testListsName.Clear();
        foreach(string name in db.GetTestListNamesFromDatabase())
        {
            _testListsName.Add(name);
        }

        if (_testListsNames.Count != 0) CanLoad = true;
    }
}

However, note that this will raise changed events on each item using the .Add() call. See here: Can I somehow temporarily disable WPF data binding changes?

Edit: from your updated code. It can also be seen that you do not set the DataContext on your ChooseFromDbTabItem. You need to bind the DataContext property to the view model that exposes the collection:

<TabItem Header="Choose" Width="250" >
    <tabData:ChooseFromDbTabItem DataContext="{Binding}" />
</TabItem>
Community
  • 1
  • 1
lenkan
  • 4,089
  • 1
  • 14
  • 12
  • @derp - Where are you initializing the list for the first time? – lenkan Jan 03 '16 at 11:26
  • In the constructor of the viewmodel – derb Jan 03 '16 at 11:28
  • @derp Can you check in the debugger what is returned from db.GetTestListNamesFromDatabase() ? Because this solution most definitely works for me. – lenkan Jan 03 '16 at 11:35
  • As previous I get list with all inserted content but unfortunately the listbox in tabitem is not refreshing – derb Jan 03 '16 at 11:38
  • Ok. Then I would need to see some more code. Sounds like there is a DataContext problem. – lenkan Jan 03 '16 at 11:46
  • Try to check your output & see if you have binding errors – Hodaya Shalom Jan 03 '16 at 12:01
  • @derb - I can't see where you are binding to the collection in the code you posted? – lenkan Jan 03 '16 at 12:15
  • @derb - There is your problem. You are not setting the DataContext on the ChooseFromDbTabItem. See my updated answer. Which 'ViewModel' class is exposing the TestListsName collection? – lenkan Jan 03 '16 at 12:32
  • @lenkan Thanks for your help. The problem was as Hodaya Shalom noticed I didn't raise OnPropertyChange in Observable Collection – derb Jan 03 '16 at 12:39
  • @derb - Yes, I know. I mentioned this in the first line of my answer. I was just trying to make you follow .NET programming guidelines. I.e., do not provide settable collection properties: https://msdn.microsoft.com/en-us/library/dn169389(v=vs.110).aspx . But this is a minor issue in view model classes, so whatever floats your boat =). – lenkan Jan 03 '16 at 12:42
1

You have to rise PropertyChanged event for the reason you change your entire collection and not a single item (if you changed a single item that was updated through the Observable).

 private ObservableCollection<string> _testListsName;
    public ObservableCollection<string> TestListsNames
    {
        get { return _testListsName; }
        set
        {
            if (_testListsName != value)
            {
                _testListsName = value;
                NotifyPropertyChanged("TestListsNames");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string property)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
Hodaya Shalom
  • 4,327
  • 12
  • 57
  • 111
  • 1
    It works, thanks. I thought the ObservableCollection rises it automatically – derb Jan 03 '16 at 12:35
  • The ObservableCollection rise if something within the list change (Add, Remove, Replace...), but if we change the list (New) we need to rise property changed. – Hodaya Shalom Jan 06 '16 at 09:23