in my Silverlight 4 project, I like to bind an ReadOnlyObservableCollection to a Button Property. The collection is part of a class, which binds to the DataContect of the UserControl, that contains the Button.
<Button Visibility="{Binding Children, Converter={StaticResource ConvertHasListItems2Visibility}}" />
The converter is called the first time, when the control is created, but after that, it isn't called when I add or remove items to the collection. I checked this with breakpoints. I even tried to bind to an ObservableCollection, but there is the same problem.
Binding to another property of my class works (my class implements INotifyPropertyChanged).
Is there anything special with binding to a (ReadOnly)ObservableCollection, that should notify, when its elements changed ( added or removed, to be exact)?
Thanks in advance,
Frank
Edith includes the declaration of the Collection(s):
public class MyClass
{
private ObservableCollection<IServiceItemVMBase> _children;
private ReadOnlyObservableCollection<IServiceItemVMBase> _childrenReadOnly;
public ViewModelBase(IServiceObjectBase serviceObject, IServiceItemVMBase parent)
{
_children = new ObservableCollection<IServiceItemVMBase>();
_childrenReadOnly = new ReadOnlyObservableCollection<IServiceItemVMBase>(_children);
}
public ReadOnlyObservableCollection<IServiceItemVMBase> Children
{
get { return _childrenReadOnly; }
}
}
Related
I am currently writing a WPF application, using a MVVM approach and Entity Framework.
In my MainVindow.xaml, I have a navigation system on the left.
For each of its navigation items, there is a corresponding user control.
Clicking on one of its items displays the user control on the right of the main window.
I have a view model for each user control.
I bind my MainWindow.xaml to an instance of my MainViewModel class.
Each user control is binded to an instance of its corresponding viewmodel which is stored in my MainViewModel class.
In the constructor of each of these user controls viewmodels, I pass the MainViewModel.
The idea is to share an entity amongst all these viewmodels:
class MainViewModel
{
public SharedEntity MySharedEntity
{
get
{
return _mySharedEntity;
}
set
{
InpcLogic(_mySharedEntity);
}
}
public FirstUserControlViewModel MyFirstUserControlViewModel
{
get
{
if (_myFirstUserControlViewModel == null)
_myFirstUserControlViewModel = new FirstUserControlViewModel(this);
return _myFirstUserControlViewModel;
}
}
}
This way, in the FirstUserControlViewModel instance, I can access MySharedEntity this way:
class FirstUserControlViewModel
{
MainViewModel _mainViewModel;
public MainViewModel MyMainViewModel
{
get
{
return _mainViewModel;
}
}
public SharedEntity MyMainViewmodelSharedEntity
{
get
{
return _mainViewModel.MySharedEntity;
}
}
public FirstUserControlViewModel(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
}
}
In my user control xaml (which is binded to the view model above), I can bind controls to this shared entity from my mainViewModel.
Now, here is my problem: When I change the shared entity from anywhere in the application, the bindings are not always refreshed on the user control level. Could it be due due to the fact that the INPC logic is in the main view model and not the user control view model?
Let's say I bind a textblock in my first user control like so:
<TextBox Text="{Binding MyMainViewModel.MySharedEntity.AnyStringMember,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
It works fine.
However, if I do the following
<TextBox Text="{Binding MyMainViewmodelSharedEntity.AnyStringMember,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
Then, bindings do not refresh when I change the shared entity. It uses the same reference to my entity in both cases, doesn't it?
**
UPDATE
**
Here's what I'll end up doing:
In my MainViewModel, I have the following property:
public class MainViewModel
{
EntityFrameworkClass _myPrivateEfObject;
EntityFrameworkNavigationPropertyClass _myPrivateEfNavigationPropertyObject;
public EntityFrameworkClass MyPublicEfObject
{
get
{
return _myPrivateEfObject;
}
set
{
InpcLogic(ref _myPrivateEfObject, value);
MyPublicEfNavigationPropertyObject = _myPrivateEfObject.EntityFrameworkNavigationPropertyClass.FirstOrDefault();
}
}
public EntityFrameworkNavigationPropertyClass MyPublicEfNavigationPropertyObject
{
get
{
return _myPrivateEfNavigationPropertyObject;
}
set
{
InpcLogic(ref _myPrivateEfNavigationPropertyObject, value);
}
}
}
Then in my user control xaml, I simply use MainViewModel.MyPublicEfNavigationPropertyObject
You need to call the method that implements INotifyPropertyChanged. In your case, you want it implemented to your MySharedEntity then on the setter of the AnyStringMember you want to RaisePropertyChanged.
I am binding my textbox to a property which does not implement any INPC behavior. Yes, it refers to the same object, but if there is no INPC behavior implemented, the graphical component can't be notified that it needs to refresh.
I am going to bind to properties within my MainViewModel and implement INPC in there.
I have a UserControl with a ComboBox in it and I'm binding an ObservableCollection to it such as follow. Right now the collection is populated in the UserControl. However, I would like to create the ObservableCollection in the MainWindow and have another constructor for my UserControl. here's what I got now and it's working:
public ObservableCollection<ComboBoxInfo> Items { get; private set; }
public CustomComboBox()
{
InitializeComponent();
Items = new ObservableCollection<ComboBoxInfo>();
cmb.ItemsSource = Items;
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
this.createNameComboBox(); // ObservatoryCollection populating
}
}
I tried implementing a second constructor and moving the collection populating function in the Main Window but I get an error saying my comboBox in the UserControl is not set to an instance of an object. Ideally I would like something like this:
public CustomComboBox(ObservableCollection<ComboBoxInfo> Items)
{
this.Items = Items
// Not sure if the binding should be done here or in default constructor
}
Any idea how to properly do this ? Thanks
Your solution should include a ViewModel which would be set as a DataContext of your User Control.
This ViewModel should contain and expose the ObservableCollection as a public property, ideally it should use some injected service provider to obtain the data from some data store and populate the ObservableCollection with that data, Finally, the ComboBox from your User Control should bind to that ObservableCollection in the ViewModel.
Your User Control code-behind should have no code other than some event handlers to manipulate the UI in response to UI events if necessary...
That is how things are done properly in WPF utilizing the MVVM pattern.
Here is an example of how a service is injected into the VM constructor and used to populate a collection with some data:
public class MainWindowViewModel
{
private ICustomerService _customerService;
public MainWindowViewModel(ICustomerService customerService)
{
_customerService = customerService;
Customers = new ListCollectionView(customerService.Customers);
}
public ICollectionView Customers { get; private set; }
}
I have model:
public class Song
{
public int ContentID { get; set; }
public bool IsSelected
{
get
{
var song = PlayerHelper.ReadNowPlaying();
return song.Id == ContentID;
}
}
}
I have a view with ListBox:
<ListBox x:Name="songsLstBox" ItemsSource="{Binding Top100Songs}" />
And ViewModel with list of Songs items. So, sometimes i want to refresh (redraw) the listbox. It's need to display that IsSelected is changed (No, i can't using INotifyPropertyChanged in model and setting it in viewmodel).
So how i can redraw a listbox in WP7? I can't find any Refresh or Update method for UIElements.
I tried calling this.OnPropertyChanged("Top100Songs"); but it doesn't work. I tried calling UpdateLayout - the same.
One way is setting DataContex for page to null and then revert to my ViewModel. It works, but is so long (about 5 secs. for changing).
So any ideas?
Write your own collection wrapper and use it for the Top100Songs property
class SongCollection : ObservableCollection<Song>
{
public Refresh()
{
OnCollectionChanged(
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
I am trying to implement a search as you type screen in my Silverlight application. The idea is that I have a screen with a textedit control and a listbox. The listbox is filled with all my data.
When the user types something in the textbox the following happens:
All the items that are not containing all the letters from the user input are hidden.
The matching letters of the visible list items are highlighted with a different color.
I am not sure how to start with this, so all pointers, samples and hints are welcome!
I would suggest using a CollectionViewSource. A CollectionViewSource has the ability to filter items. You can bind your ListBox to the CollectionViewSource and handle Filter event to do the filtering. Bind your "Search Box" to a Text property which you can use in your Filter event. You can handle the "KeyUp" event of the TextBox control to kick off your filtering, by calling the Refresh method on the CollectionViewSource View.
Filtering Data using CollectionViewSource: http://xamlcoder.com/blog/2010/10/27/filtering-data-using-collectionviewsource/
http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource.filter.aspx
http://msdn.microsoft.com/en-us/library/system.componentmodel.icollectionview.aspx
http://timheuer.com/blog/archive/2009/11/04/updated-silverlight-3-datagrid-grouping-data-pagedcollectionview.aspx
http://bea.stollnitz.com/blog/?p=392
Sudo code:
// ViewModel - properties should fire NotifyPropertyChanged
public class ViewModel : INotifyPropertyChanged
{
public ViewModel
{
this.Data = new CollectionViewSource();
this.Data.Source = this.GenerateObjects();
this.Data.Filter += (s,e) =>
{
// TODO: add filter logic
DataObject item = e.Item as DataObject;
return item.Name.Contains(this.SearchText);
};
}
public string SearchText{get;set;}
public CollectionViewSource Data {get;set;}
private List<DataObject> GenerateObjects(){ // generate list of data objects }
}
// View XAML
<StackPanel>
<TextBox Text="{Binding SearchText, Mode=TwoWay}" KeyUp="OnKeyUp"/>
<ListBox ItemsSource="{Binding Data.View}"/>
</StackPanel>
// View Code Behind
public class View : UserControl
{
public View() { this.DataContext = new ViewModel(); }
private ViewModel ViewModel { get { return this.DataContext as ViewModel; } }
private OnKeyUp()
{
this.ViewModel.Data.View.Refresh();
}
}
You may want to start with the AutocompleteBox from the Silverlight Toolkit.
It has a number of handy points where you would be able to extend it's functionality, for example in the instance searching your pool of values.
I have an INotifyProperty Screen item that I have bound to a wpf control.
Ok... I Simplified everything and am posting more code. I have a MainViewModel with the selected screen property.
public Screen SelectedScreen
{
get { return this.selectedScreen; }
set
{
this.selectedScreen = value;
this.OnPropertyChanged("SelectedScreen");
}
}
I have a textbox that is bound to this property:
<TextBlock Text="{Binding Path=SelectedScreen.ScreenNumber}" />
This all works initially. I have created another control that is changing the selected screen with the following code.
public Screen SelectedScreen
{
get { return (Screen)GetValue(SelectedScreenProperty); }
set
{
this.SetValue(SelectedScreenProperty, value);
for (int x = 0; x < this.Screens.Count; ++x)
this.Screens[x].IsSelected = false;
value.IsSelected = true;
}
}
public ObservableCollection<Screen> Screens
{
get { return (ObservableCollection<Screen>)GetValue(ScreensProperty); }
set { this.SetValue(ScreensProperty, value); }
}
public static readonly DependencyProperty SelectedScreenProperty =
DependencyProperty.Register("SelectedScreen",
typeof(Screen),
typeof(ScreenSelection));
public static readonly DependencyProperty ScreensProperty =
DependencyProperty.Register("Screens",
typeof(ObservableCollection<Screen>),
typeof(ScreenSelection),
new UIPropertyMetadata(new ObservableCollection<Screen>()));
This screen selection control is working. When I change screens and put a breakpoint on the set property of SelectedScreen it is called which then calls the SelectedScreen property of the MainViewModel. So the event is firing, but the textbox isn't updated even though it binds correctly the first time.
Does the class which contains the SelectedScreen property implement INotifyPropertyChanged? When the SelectedScreen property changes, the containing class should raise the PropertyChanged event, and typically, WPF should update the Binding.
Thank you gehho for looking at this. I figured it out and there is no way you had enough information to be able too. I was inheriting from ViewModelBase in the MainViewModel that was inheriting from ObservableObject where I implemented INotifyPropertyChanged. The problem is that I implemented the methods for INotifyPropertyChanged in both classes and WPF was listening to the wrong one. Very obscure. Very annoying. Very lasjkdf;ashdoh