I have the ListBox on my MainView.xaml, selecting the Item forces the ContentControl to display different UserControls. I use Caliburn.Micro library in this propgram. Here's some code:
<ListBox Grid.Row="1" Grid.Column="1" x:Name="ItemsListBox" SelectedItem="0" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding TextBlock1Text}" x:Name="TextBlock1"/>
<ContentControl Grid.Row="3" Grid.Column="1" Content="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content}" />
The MainViewModel.cs:
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
NotifyOfPropertyChange(() => Name);
}
}
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public MainViewModel()
{
TextBlock1Text = "Test";
Items = new ObservableCollection<ItemsModel>()
{
new ItemsModel { Name="Useless", Content=null },
new ItemsModel { Name="TextChangerViewModel", Content=new TextChangerViewModel(TextBlock1Text) }
};
}
public ObservableCollection<ItemsModel> Items { get; set; }
The ItemsModel.cs:
public class ItemsModel
{
public string Name { get; set; }
public object Content { get; set; }
}
And finally the TextChangerViewModel.cs:
public class TextChangerViewModel : Conductor<object>
{
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public TextChangerViewModel(string textBlock1Text) //passing parameter from another ViewModel
{
TextBlock1Text = textBlock1Text;
}
}
So, the main question is how to change the TextBlock1Text (and the Text value of TextBlock in .xaml as well) in the MainViewModel.cs from the TextChangerViewModel.cs? I was thinking about using something like NotifyCollectionChanged on my Items ObservableCollection, but it work with collection of ItemsModel, not with the VM's, so I'm stuck here.
I'm also not sure if having public object Content { get; set; } in ItemsModel.cs is a good thing if I'm targeting the MVVM pattern, but I don't know the other way to do it (I'm very new to MVVM).
UPD
I'm looking for the property-changing way because I need to change the TextBlock1Text Text from another UserControl. Suppose I have the button on my TextChangerView.xaml: <Button Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="Change da text" cal:Message.Attach="ChangeTextButton"/>
And after the click on it I want the text on the parental MainView.xaml to change. But the thing is, I don't know how to change properties in this case, as I wrote above why.
Change the the binding of textblox1 to reference the selected item.
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Name}" x:Name="TextBlock1"/>
or
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content.TextBlock1Text}" x:Name="TextBlock1"/>
Related
To preface this I am new to WPF, XAML and C#
I have been looking for that last two days now for a method to create a custom class object that will expose some properties. I then want to bind some text boxes in the UI to those properties but I want to be able to do this for multiple instances of a class (Analog in my case). I am sure this is something simple but I am not understanding how to approach this.
Example of the class:
class Analog
{
public string LblA0
{ get; set; }
}
EDIT:
I have figured out how to bind to a class but I am unsure how I would create another class instance to bind to separately.
<Grid>
<Grid.DataContext>
<local:Analog/>
</Grid.DataContext>
<TextBlock x:Name="labelAnalog0"
Text="{Binding LblA0, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
EDIT 2: ViewModel class?
class ViewModel
{
public Analog Analog0
{ get; set; }
}
Okay thanks to Clemens I think I understand how to solve this.
Here are the classes I have made:
class Analog
{
public Analog(string s)
{
lblA = s;
}
private string lblA;
public string LblA
{
get
{
return lblA;
}
set
{
lblA = value;
}
}
}
class ViewModel
{
Analog analog0 = new Analog("test");
Analog analog1 = new Analog("test2");
public Analog Analog0
{
get
{
return analog0;
}
}
public Analog Analog1
{
get
{
return analog1;
}
}
}
XAML
<TextBlock x:Name="labelAnalog0"
Text="{Binding Analog0.LblA, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock x:Name="labelAnalog1"
Text="{Binding Analog1.LblA, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
The call to change the datacontext in the mainpage
analogGrid.DataContext = new ViewModel();
I've created a ListBox with this structure:
<ListBox VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfo}">
how you can see I binded the EventInfo property that I valorize behind code. This property have the OnPropertyChange(); implementation as my other properties, and the value setted is got correctly. Anyway, I'm not able to display the binded source:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
<TextBlock Text="test" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
now the property League value isn't displayed also the value test. I really don't understand why. The League property exist, and also I've no error in xaml.
What I did wrong?
UPDATE:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
and in the Model
public class EventInfo
{
public string League { get; set; }
public string Date { get; set; }
public string GameWeek { get; set; }
public string GameStart { get; set; }
public string FirstTime { get; set; }
public string SecondTime { get; set; }
public string Stadium { get; set; }
public List<MatchArbiter> Arbiter { get; set; }
}
Try this. You need to populate ItemsSource with a collection, not a single item. Instead of your existing EventInfo property, you need a collection property. I'm going to rename it to EventInfoItems to keep confusion to a minimum.
private ObservableCollection<Models.EventInfo> _eventInfoItems =
new ObservableCollection<Models.EventInfo>();
public ObservableCollection<Models.EventInfo> EventInfoItems
{
get { _eventInfoItems; }
set
{
_eventInfoItems = value;
OnPropertyChanged();
}
}
Now, somewhere, you're going to have to add some items to that collection if you want anything to appear in the list. You could create a few test items in your viewmodel constructor, just for the time being. Like this:
EventInfoItems.Add(new EventInfo { League = "NBA" });
EventInfoItems.Add(new EventInfo { League = "Premier League" });
EventInfoItems.Add(new EventInfo { League = "Serie A" });
XAML
<ListBox
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfoItems}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Update
Turns out OP may have only one item. If that's the case, a ListBox is unnecessary. A ContentControl is the right control when you've got only one item and you want to display it with a DataTemplate. This XAML will use the original version of the EventInfo property:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
XAML:
<ContentControl
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Content="{Binding EventInfo}"
>
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
Edit: The basic problem is binding a List to ListBox(or any other control). So I am editing the question.
I bound a list of string to a ListBox as below. However when I change the contents of the textbox it is not changing the string in the source list.Why?
public partial class MainWindow : Window
{
List<string> _nameList = null;
public List<string> NameList
{
get
{
if (_nameList == null)
{
_nameList = new List<string>();
}
return _nameList;
}
set
{
_nameList = value;
}
}
public MainWindow()
{
NameList.Add("test1");
NameList.Add("test2");
InitializeComponent();
}
And the XAML
<ListBox Grid.Row="0" Grid.Column="0" DataContext="{Binding ElementName=main}" ItemsSource="{Binding NameList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding .,Mode=OneWayToSource , UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The DataContext of each ListBoxItem is the string itself, so the path of your binding is empty (.). TwoWay and OneWayToSource bindings require a path, since you can't just replace the current DataContext. So you need to wrap your string in an object that exposes the string as a property:
public class StringItem
{
public string Value { get; set; }
}
Expose the strings as a list of StringItem:
public partial class MainWindow : Window
{
List<StringItem> _nameList = null;
public List<StringItem> NameList
{
get
{
if (_nameList == null)
{
_nameList = new List<StringItem>();
}
return _nameList;
}
set
{
_nameList = value;
}
}
public MainWindow()
{
NameList.Add(new StringItem { Value = "test1" });
NameList.Add(new StringItem { Value = "test2" });
InitializeComponent();
}
And bind to the Value property:
<ListBox Grid.Row="0" Grid.Column="0" DataContext="{Binding ElementName=main}" ItemsSource="{Binding NameList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note that StringItem will also need to implement INotifyPropertyChanged so that bindings are automatically updated. You should also expose the list as an ObservableCollection<T> rather than a List<T>
May be it helsp?
<ListBox Name="lsbList">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
you can create a DataGridTemplateColumn.CellEditingTemplate with an itemscontrol and textboxes to edit your items
If I didn't misunderstand your question, it is pretty easy to implement. Look:
<ComboBox Text="My Comment 5 with addition." IsEditable="True" Height="25" Width="200">
<ComboBoxItem>My comment1</ComboBoxItem>
<ComboBoxItem>My comment2</ComboBoxItem>
</ComboBox>
I'm facing a problem in my WPF project at the moment. At this moment I have a Viewmodel which has a Manager (to communicate with the repo).
internal class TicketViewModel
{
private TicketManager mgr;
public IEnumerable<Ticket> List { get; set; }
public TicketViewModel()
{
mgr = new TicketManager();
List = mgr.GetTickets();
}
}
I've managed to bind this list to the Listbox in my MainWindow. The next step is that I need to add an extra ticket to the list and also pass this through the manager. The problem is I need two parameters from some Controls in the MainWindow. From MVVM perspective I need to use bound Commands on e.g. a Button to communicate with the viewmodel as my viewmodel can't/may not access controls from the window. Is using parameterized Commands the way to go here?
The next problem is that the Listbox won't update I guess. This is the code:
<ListBox x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I found that using a CompareableCollection is the way to go here, but then I still have to read all the Tickets again after adding a new Ticket.
Thanks in advance,
Hicy
okay here is the code.
Lets say you have three textboxes on MainWindow(since you have three Textblocks.) so Your MainWindow.xaml looks like
<Window.DataContext>
<local:MyViewModel/>--set's your viewModel
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="250*"/>
<RowDefinition Height="90"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="TicketNumber" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=Text}" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="Text" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=State}" />
<TextBox x:Name="State" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=TicketNumber}" />
<Button Content="Button" Command="{Binding Path=MainCommand}" Grid.Row="2"/>
</Grid>
and I am assuming that you have some class called class Ticket which contain these three members
Class Ticket
{
public int TicketNumber { get; set; }
public string Text { get; set; }
public string State { get; set; }
}
Now in class TicketManager we fill it with some dummy data
class TicketManager
{
ObservableCollection<Ticket> tl = new ObservableCollection<Ticket>();
internal ObservableCollection<Ticket> GetTickets()
{
tl.Add(new Ticket() { State = "State1", Text = "Text1", TicketNumber = 1 });
tl.Add(new Ticket() { State = "State2", Text = "Text2", TicketNumber = 2 });
tl.Add(new Ticket() { State = "State3", Text = "Text3", TicketNumber = 3 });
return tl;
}
}
and in your Mainwindow ViewModel lets call it MyViewModel.cs we add
class MyViewModel:INotifyPropertyChanged
{
private TicketManager mgr;
public ObservableCollection<Ticket> List { get; set; }
private string text;
private string state;
private int ticketNumber;
private readonly DelegateCommand<object> MyButtonCommand;
public Class1()
{
mgr = new TicketManager();
List = mgr.GetTickets();
MyButtonCommand = new DelegateCommand<object>((s) => { AddListToGrid(text, state, ticketNumber); }, (s) => { return !string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(state); });
}
private void AddListToGrid(string text, string state, int ticketNumber)
{
List.Add(new Ticket() {Text=text,State=state,TicketNumber=ticketNumber });
}
public DelegateCommand<object> MainCommand
{
get
{
return MyButtonCommand;
}
}
public string Text
{
get
{
return text;
}
set
{
text = value;
OnPropertyChanged("Text");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public string State
{
get
{
return state;
}
set
{
state = value;
OnPropertyChanged("State");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public int TicketNumber
{
get
{
return ticketNumber;
}
set
{
ticketNumber = value;
OnPropertyChanged("TicketNumber");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can Modify the code in anyway you want
This ViewModel implements fewthings which are very important from MVVM point of view
1) INotifyPropertyChanged
2) WPF Delegate Command
P.S:The code is tested and it runs as expected
Don't get hung up on MVVM it is simply a separation of data from a view, and models are shared between the two with a majority of the business logic (on a shared component) should be performed on the VM; it is not a religion just a three tiered data system. IMHO
If your button needs to do an operation, have it make a call, most likely in the code behind, to a method on the VM which handles the business logic, updates the list with the new item and notifies the manager.
I would bind the list in question to an ObservableCollection which can notify upon insert/delete of an item.
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);
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
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
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);