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.
Related
so I am trying to use the CheckBox Control with MVVM logic. However the only event existing is CheckedChanged, which also triggers if I am navigating from the page for example.
I am searching for a Clicked event, like a button has.
Do I need to create a custom control with custom renderer, or is there a better solution?
Looking forward to replies.
[EDIT]
To provide more information about my issue, I will put code examples below.
My CheckBoxes are inside a ListView. Each CheckBox is defined like this in XAML:
<CheckBox
IsChecked="{Binding IsCompleted}">
<CheckBox.Behaviors>
<prism:EventToCommandBehavior
EventName="CheckedChanged"
Command="{Binding SubTaskStateChangedCommand}"/>
</CheckBox.Behaviors>
</CheckBox>
The ItemSource of the ListView has a property IsCompleted and a DelegateCommand SubTaskStateChangedCommand. It is worth mentioning that i am using Prism.
In the constructor:
SubTaskStateChangedCommand = new DelegateCommand(OnSubTaskStateChangedCommandExecuted);
Class SubTaskModel:
public DelegateCommand SubTaskStateChangedCommand { get; set; }
private void OnSubTaskStateChangedCommandExecuted()
{
//Do something
}
private bool _isCompleted;
public bool IsCompleted
{
get { return _isCompleted; }
set { _isCompleted = value; OnPropertyChanged(); }
}
So when I am navigating to another page in my ViewModel using the Prism NavigationService, the OnSubTaskStateChangedCommandExecuted Method gets triggered. However, I noticed that when this happens, the IsCompleted value DOES NOT change.
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've been exploring the Caliburn Micro MVVM Framework just to get a feel for it, but I've run into a bit of a problem. I have a TextBox bound to a string property on my ViewModel and I would like the property to be updated when the TextBox loses focus.
Normally I would achieve this by setting the UpdateSourceTrigger to LostFocus on the binding, but I don't see any way to do this within Caliburn, as it has setup the property binding for me automatically. Currently the property is updated every time the content of the TextBox changes.
My code is very simple, for instance here is my VM:
public class ShellViewModel : PropertyChangeBase
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyOfPropertyChange(() => Name);
}
}
}
And inside my view I have a simple TextBox.
<TextBox x:Name="Name" />
How to I change it so the Name property is only updated when the TextBox loses focus, instead of each time the property changes?
Just set the binding explictly for that instance of the TextBox and Caliburn.Micro won't touch it:
<TextBox Text="{Binding Name, UpdateSourceTrigger=LostFocus}" />
Alternatively, if you want to change the default behaviour for all instances of TextBox, then you can change the implementation of ConventionManager.ApplyUpdateSourceTrigger in your bootstrapper's Configure method.
Something like:
protected override void Configure()
{
ConventionManager.ApplyUpdateSourceTrigger = (bindableProperty, element, binding) =>{
#if SILVERLIGHT
ApplySilverlightTriggers(
element,
bindableProperty,
x => x.GetBindingExpression(bindableProperty),
info,
binding
);
#else
if (element is TextBox)
{
return;
}
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
#endif
};
}
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