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));
}
}
Related
I am new to wpf and MVVM, and I've spent all day trying to get the value of a ComboBox to my ViewModel on SelectionChanged. I want to call a function in the selection changed process. In mvvm, what is the solution for it?
In MVVM, we generally don't handle events, as it is not so good using UI code in view models. Instead of using events such as SelectionChanged, we often use a property to bind to the ComboBox.SelectedItem:
View model:
public ObservableCollection<SomeType> Items { get; set; } // Implement
public SomeType Item { get; set; } // INotifyPropertyChanged here
View:
<ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding Item}" />
Now whenever the selected item in the ComboBox is changed, so is the Item property. Of course, you have to ensure that you have set the DataContext of the view to an instance of the view model to make this work. If you want to do something when the selected item is changed, you can do that in the property setter:
public SomeType Item
{
get { return item; }
set
{
if (item != value)
{
item = value;
NotifyPropertyChanged("Item");
// New item has been selected. Do something here
}
}
}
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; }
}
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; }
}
}
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