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
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 am using WPF and I have a custom datagrid. What I would like to do is add a property (CustomGridCommands) to that grid which I can set in xaml from any view.
What I have at the moment is as follows (I have changed the code a bit to simplify it):
Custom grid C# code:
public class CustomWPFDataGrid : DataGrid, INotifyPropertyChanged
{
public static readonly DependencyProperty CustomContextMenuCommandsProperty =
DependencyProperty.Register("CustomContextMenuCommands",
typeof (ObservableCollection<WPFBaseCommand>),
typeof (CustomWPFDataGrid));
[Bindable(true)]
public ObservableCollection<WPFBaseCommand> CustomContextMenuCommands
{
get { return (ObservableCollection<WPFBaseCommand>) GetValue(CustomContextMenuCommandsProperty); }
set { SetValue(CustomContextMenuCommandsProperty, value); }
}
...
...
}
XAML code:
<common:CustomWPFDataGrid
ItemsSource="{Binding Path=ItemList}"
CustomContextMenuCommands="{Binding Path=CustomGridCommands, Mode=TwoWay}">
....
</common:CustomWPFDataGrid >
The object I have bound to the view that contains the grid is as follows:
public class TestViewModel
{
public ObservableCollection<TestDisplayViewModel> ItemList { get; set; }
public ObservableCollection<WPFBaseCommand> CustomGridCommands;
public TestViewModel()
{
... population of objects here
}
When I run this, and check the value of the property (CustomContextMenuCommands) in the datagrid, it is always null.
Any ideas what I am doing wrong?
EDIT
The setter of the "CustomContextMenuCommands" is never hit.
CustomGridCommands in your ViewModel is a field, View cannot use it. If you make it a public property, then it will become accessible. More details on what can be used as binding source can be found on MSDN - Binding Sources.
If using WPF 4.5, static properties can also be used for binding, as described in release notes.
Blacklight is an older set of WPF controls and styles. The code can be found here. It contains a control called AnimatedExpander which isn't really an expander, rather it just implements HeaderedContentControl and adds an IsExpandedProperty dprop:
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register("IsExpanded", typeof(bool), typeof(AnimatedExpander), new PropertyMetadata(true));
public bool IsExpanded
{
get
{
if (this.expandToggleButton != null)
{
return this.expandToggleButton.IsChecked.Value;
}
return (bool)GetValue(IsExpandedProperty);
}
set
{
SetValue(IsExpandedProperty, value);
}
}
I need to bind to IsExpanded so that I can persist whether expanders are expanded. I'm pretty sure I have the binding setup correctly, and that there is a problem with this custom dependency property. If I open the view in Snoop, and set the IsExpanded=True on the expander, the binding works. However, just clicking the expandToggleButton on the control only expands the control, it doesn't hit my binding.
<controls:AnimatedExpander IsExpanded="{Binding SGGExpanderExpanded}" />
private bool _sGGExpanderExpanded;
public bool SGGExpanderExpanded
{
get { return _sGGExpanderExpanded; }
set
{
if (_sGGExpanderExpanded != value)
{
_sGGExpanderExpanded = value;
OnPropertyChanged("SGGExpanderExpanded");
}
}
}
How can I bind to a value that changes when the user clicks the toggle button that is wired to expand the control?
A bad solution:
I was able to make this work by attaching an event to the ToggleButton click and looking at the "sender" Content and IsChecked values to update my viewmodel.
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
};
}