How to refresh a databound TreeView? - wpf

I have two TreeView´s in a TabControl, databound to a xmlDataProvider. If i add nodes to my Xml and save it:
xmlDataProvider.Document.Save(fullPathToXml);
xmlDataProvider.Refresh();
Only the TreeView that is not in the open Tab refresh. Both TreeView´s look like this:
<TreeView Name="DIFFERENT_NAMES" ItemsSource="{Binding Source={StaticResource dataxml}, XPath=./*}"/>

If your ItemsSource is binded to a property implementing OnPropertyChanged, you can add the attribute "UpdateSourceTrigger=PropertyChanges" to your binding in the XAML.
Hence, the control will update itself each time OnPropertyChanged is called
EDIT
I assume your ViewModel already implements OnPropertyChanged
Therefore, all you have to do is, when you declare your property:
private XmlDataProvider _xmlDataProvider;
public XmlDataProvider XmlDataProvider
{
get { return xmlDataProvider; }
set
{
xmlDataProvider = value;
OnPropertyChanged("XmlDataProvider");
}
}
Initialize the XmlDataProvider in your constructor, and then, each time the object is modified, it will call the method OnPropertyChanged, on the property you specify (here "XmlDataProvider"), and each time OnPropertyChanged is called, your view bound to this object will refresh automatically :)

Related

WPF MVVM ListBox/ComboBox ItemsSource won't update UI from ViewModel

i have a container such as a listbox, combobox etc that its ItemsSource property is bound to an observable collection in my view model.
When i'm trying to add/remove items from the collection via some method in my VM it won't reflect in the UI,
The only way the UI would actually refresh is if i assign the collection a new value (i.e another collection with the relevant data) which forces him to re-bind the whole collection.
maybe i'm missing/don't understand something about the collection binding issue, either way if someone has a solution/good explanation/both it would be great.
here is a sample from my View(in this case its a listbox)
<ListBox
Grid.Row="9"
Grid.Column="1"
Grid.ColumnSpan="3"
Width="200"
Height="200"
ItemsSource="{Binding PreSavedRecordingScheduleList,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedPreSavedRecordingSchedule,UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Display"/>
and here is my ViewModel:
private ObservableCollection<ScheduledRecordingObject> m_PreSavedRecordingScheduleList;
PreSavedRecordingScheduleList = new ObservableCollection<ScheduledRecordingObject>();
public ObservableCollection<ScheduledRecordingObject> PreSavedRecordingScheduleList
{
get
{
return m_PreSavedRecordingScheduleList;
}
set
{
m_PreSavedRecordingScheduleList = value;
OnPropertyChanged("PreSavedRecordingScheduleList");
}
}
ScheduledRecordingObject also implements INotifyPropertyChanged.
viewmodel
public ObservableCollection<yourType> MyItemsSource {get;set}
initialize once in contructor and use clear, add and remove to alter it
view
<ListBox ItemsSource="{Binding MyItemsSource}"/>
just be sure that the right DataContext is set.
thats how it should look in your code
EDIT:
some hints to your posted code:
//remove the UpdateSourceTrigger=PropertyChanged - makes no sense the Mode is OneWay anyway :)
ItemsSource="{Binding PreSavedRecordingScheduleList}"
//the following line should just called once and at best in ctor
//but the binding will of course work too when you assign a new collection
PreSavedRecordingScheduleList = new ObservableCollection<ScheduledRecordingObject>();
all in all your code looks good, and if the viewmodel is the Datacontext of your Listbox then it should work. let me know what Snoop is showing :)
Remove the OnPropertyChanged("PreSavedRecordingScheduleList"); from the ObservableCollection. Actually you don't need a backing field. Attach the CollectionChanged event on the ObservableCollection, something like this
1- Inside the ViewModel constructor attach the event CollectionChanged
PreSavedRecordingScheduleList = new ObservableCollection<ScheduledRecordingObject>();
PreSavedRecordingScheduleList.CollectionChanged += PreSavedRecordingScheduleList_CollectionChanged;
2- Inject the OnPropertyChanged("PreSavedRecordingScheduleList") in the event handler
void PreSavedRecordingScheduleList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged("PreSavedRecordingScheduleList");
}

How to bind XmlDataProvider from ViewModel to ListBox?

I'm having trouble binding XmlDataProvider from ViewModel (my DataContext) to ListBox's ItemsSource.
This is what I did -
In Xaml -
<ListBox ItemsSource="{Binding Path=SelectedXmlProvider}">...</ListBox>
In my ViewModel class -
public XmlDataProvider SelectedFeedXmlProvider
{
get { return _selectedFeedXmlProvider; }
set
{
_selectedFeedXmlProvider = value;
RaisePropertyChanged("SelectedFeedXmlProvider");
}
}
In my application -
viewModel.SelectedFeedXmlProvider = new XmlDataProvider
{
Source = new Uri("http://sxp.microsoft.com/feeds/3.0/MSDNTN/CSharpHeadlines"),
XPath = "//item"
};
And... ListBox is EMPTY! (where it should have been full with data).
When setting the ListBox's ItemsSource to an XmlDataProvider that's defined as a static resource, all is fine. Thing is, I must use MVVM, and I need to be able to dynamically change the XML source.
Can someone help? what's wrong with the code above? why is the list empty?
Thanks!

Error binding a dependency property of a user control to a property on the parent's view model

I have a user control that contains three checkboxes and three date pickers. For example, one of the date pickers on the user control looks like this (irrelevant properties like Width, etc removed for clarity)...
<telerik:RadDatePicker DisplayFormat="Long"
SelectedValue="{Binding DepositPaidDate, Mode=TwoWay}"/>
The view model for the control has a public property called PaidDate that is of type PaidDate (yup, the property and the class have the same name), the top-level Grid on the control has its DataContext set to the PaidDate property, and the individual controls in the Grid are bound to properties on this PaidDate object.
When this control is used on a window, and the window's code behind sets the PaidDate property on the control's VM explicitly, it all works fine. For example, I created a test window, whose constructor looked like this...
public PaidDateWindow(PaidDate paidDate, string windowTitle) {
InitializeComponent();
((PaidDateControlViewModel)PaidDateCtrl.DataContext).PaidDate = paidDate;
Title = windowTitle;
}
...and this worked just fine. I could show the window, and the data was displayed correctly.
The problem comes when I try to set this via a dependency property on the control. The dependency property in the user control's code behind looks like this...
public static readonly DependencyProperty PaidDateProperty = DependencyProperty.Register("PaidDate", typeof(PaidDate), typeof(PaidDateControl), new FrameworkPropertyMetadata(SetPaidDateStatic));
private static void SetPaidDateStatic(DependencyObject d, DependencyPropertyChangedEventArgs e) {
(d as PaidDateControl).SetPaidDate((PaidDate)e.NewValue);
}
private void SetPaidDate(PaidDate paidDate) {
if (DataContext != null) {
((PaidDateControlViewModel)DataContext).PaidDate = paidDate;
}
}
public PaidDate PaidDate {
get {
return (PaidDate)GetValue(PaidDateProperty);
}
set {
SetValue(PaidDateProperty, value);
}
}
As you can see, the dependency property just passes the PaidDate object through to the view model, which has the same effect as when I did this manually in the previous bit of code.
When I try to bind this dependency property to a property on the window's view model, I get a binding error. Here is the XAML in the parent window...
<vrtSystemsUserControls:PaidDateControl
PaidDate="{Binding Path=VRTSystem.PaidDate, Mode=TwoWay}" />
The parent window's VM contains a property called VrtSystem, and plenty of other controls on the window are bound to properties on that. VrtSystem also contains a property called PaidDate, and that is what I want to pass to the user control.
However, when I run this, I get the following binding error...
System.Windows.Data Error: 40 : BindingExpression path error:
'VRTSystem' property not found on 'object' ''PaidDateControlViewModel' (HashCode=18319327)'.
BindingExpression:Path=VRTSystem.PaidDate; DataItem='PaidDateControlViewModel' (HashCode=18319327);
target element is 'PaidDateControl' (Name=''); target property is 'PaidDate' (type 'PaidDate')
Now it looks to me as though WPF is passing the actual binding information through to the user control, instead of the PaidDate object, as the error says it is trying to find a VrtSystem property on the user control's VM. I have no idea why it would be doing that, as I thought the idea of the binding was to resolve the binding at the window level, and then send the results (ie the PaidDate object) in to the dependency property, where it would be sent to the VM.
I hope I've explained this clearly. Anyone able to see what's gone wrong?
Thanks for any help.
When your binding is being resolved, it is looking for the VRTSystem property on the DataContext of the control it is being applied to.
The 'DataContext' property is being inherited by child-controls so if you set a DataContext on a Window all of its children will have the same DataContext. If however one of the children itself has a different DataContext applied, all of its children will use that.
In your case, the Window has a DataContext, but so has the UserControl. So by default all bindings on the UserControl or it's chilren, will expect to find the VRTSystem property on the UserControls DataContext which is not what you want in this case.
So to explicitly target the DataContext of the Window, you have to tell the binding, by setting its RelativeSource property like this:
{Binding Path=DataContext.VRTSystem.PaidDate, Mode=TwoWay,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}

How can the ViewModel request an update in the View in WPF/MVVM?

I have a dependency property on a control in my View that is bound to a field on my ViewModel. When the user clicks a menu item I want the control to update the value of that property so the ViewModel can save it in an XML file. What is the correct mechanism to have the ViewModel request that the View update that property?
Generally with MVVM controls update their bound properties (not fields) immediately as they are edited. The ViewModel is the "state", the View is just one way of seeing that state.
Your control should update the ViewModel whenever it is edited. Your ViewModel can then save it to XML when the menu command is invoked.
I had the problem that the viewmodel was not updated when clicking on a menuitem right after writing in a TextBox.
With the parameter UpdateSourceTrigger=PropertyChanged, it worked for TextBoxes:
<TextBox Grid.Column="5" Grid.Row="7" Text="{Binding SelectedPerson.Room, UpdateSourceTrigger=PropertyChanged}"></TextBox>
But unfortunately not for DatePickers...
The strange thing is that when clicking on a button instead of the menuitem, the DatePicker is updating the viewmodel.
As I don't have more time to look for a bugfix right now, I'll just change my menuitems into buttons.
Edit: the Problem is not the menuitem but the menu itself. When I move the menuitems out of the menu, it works.
Your object must implement INotifyPropertyChanged interface and your properties should look like this
private string _property;
public string Property
{
get { return _property; }
set
{
if(_property == value) return;
_property = value;
RaisePropertyChanged("Property");
}
}
so every change made to the property will be cascaded to view through the binding mechanism.
The menu item command property will be bound to a command declared in the view model and it will trigger a method on view model and set the property value. The change will be cascaded to view:
menuItem.Click -> menuItem.Command.Execute -> viewModel.method -> change the view model property -> raise property changed event -> view property changed through binding

Binding to DependencyProperty in UserControl

I have a form with two different UserControls - one that contains a Telerik RadGridView and the other that that contains a Telerik DataForm.
The grid usercontrol is bound to a ViewModel that includes a property that exposes the Items collection that the grid is bound to.
When I bind the form to that property, everything works fine.
But I need to access additional info in the form control that really doesn't belong in the grid control's viewmodel.
So I thought I'd add a property to the form usercontrol, and bind it to the items collection:
<local:FormControl x:Name="formControl"
ItemsSource="{Binding items}"
/>
In the form's code-behind, I added a normal property:
private object itemsSource;
public object ItemsSource
{
get { return this.itemsSource; }
set { this.itemsSource = value; }
}
And this didn't work, of course. I got errors about having to use a DependencyProperty. Which I thought was reassuring - the page was actually trying to bind to the property I thought it should.
So I converted this to a DependencyProperty:
public static DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(FormControl));
public object ItemsSource
{
get { return GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
That compiled and ran without errors. Except, of course, that the control's viewmodel didn't have any items. So next was to try to pass the ItemsSource property to the form control's viewmodel:
public FormControl()
{
InitializeComponent();
this.DataContext = new FormControlVM(this.ItemsSource);
...
And this didn't work. ItemsSource was null when FormControl() was constructed. So I added a setItemsSource() method to the viewmodel, and called it in the ItemsSource property's set function. This didn't work, either. The xaml is apparently binding to the property, but it seems to do it without calling the property's set function.
So I decided to listen to the ValueChanged event, on the DependencyProperty:
public FormControl()
{
InitializeComponent();
this.DataContext = new FormControlVM();
DependencyPropertyDescriptor prop =
DependencyPropertyDescriptor.FromProperty(FormControl.ItemsSourceProperty, this.GetType());
prop.AddValueChanged(this, delegate
{
FormControlVM formControlVM = (FormControlVM)this.DataContext;
formControlVM.setItemsSource(this.ItemsSource);
});
...
And it's still not working. The ValueChanged delegate never seems to get called.
This seems like it should be a simple thing, but I've not been able to find examples on-line, and I've tried every combination I can conceive of.
Any ideas as to how I should handle this?
============ Additional info on the binding ============
Will was asking for info on the xaml that does the binding.
If I put this in page that contains the user control, binding the user control to the viewmodel of the page:
<local:FormControl x:Name="formControl"
Grid.Column="2"
DataContext="{Binding}"
/>
And then this in the user control, binding the form in the user control to the itemsCollection of the page's viewmodel:
<telerik:RadDataForm
ItemsSource="{Binding itemsCollection}"
Header="View Item:"
CommandButtonsVisibility="None"
AutoGenerateFields="False"
/>
Then everything works fine.
But the problem is that I can't bind the user control to the viewmodel of the page. I need to have the user control's viewmodel expose information that the page's viewmodel should not be seeing.
And I can't have the form in the user control reaching outside of the user control, binding to a property on the page's viewmodel. It's a violation of encapsulation, that will make using the user control on a different page much more complicated, and severely limit how I might modify the internals of the control in the future. (The page should not know anything about the controls within the user control, the controls within the user control should not know anything about the page.)
When I include the user control on a page, I want to bind the user control to a property of the page's viewmodel, and I want any controls within the user control to bind to properties of the user control, or of the user control's viewmodel.
I'd think this was a fairly common occurrence. But I've not been able to find any examples of how it might be done.
To restate the problem: I have a UserControl, that contains an embedded Telerik DataForm control. I need to bind the ItemsSource property of the embedded DataForm to a property of the DataContext of the page on which my UserControl is placed.
If the UserControl did not have its DataContext set, it would inherit the DataContext of the page, and I could easily bind the embedded DataForm's ItemsSource property to a property of it, but the UserControl has it's own DataContext.
If the UserControl was written to be used only on this page, I could bind the embedded DataForm's ItemsSource property to a property of the page, using RelativeSource binding. But this UserControl is intended to be used in a number of places, and cannot have silent dependencies on properties outside of the UserControl. I need to make the dependency explicit.
Creating a DependencyProperty on the UserControl is the right approach, but there's no need to try to replicate the property in the UserControl's viewmodel.
What I need to do is to
1: Add a DependencyProperty to the UserControl:
public QueryableCollectionView ItemsSource
{
get { return (QueryableCollectionView)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(QueryableCollectionView), typeof(FormControl));
Note: I do not need to implement a call-back function.
2: Bind the embedded DataForm's ItemsProperty to this new DependencyProperty of the UserControl, using RelativeSource binding:
<UserControl
...>
<telerik:RadDataForm
ItemsSource="{Binding Path=ItemsSource, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
/>
</UserControl>
3: Make the viewModel of the page visible, with the proper type (DataContext has type object):
public partial class MainWindow : Window
{
public MainWIndow()
{
this.InitializeComponent();
this.DataContext = new MainWindowVM();
}
public MainWindowVM viewModel
{ get { return this.DataContext as MainWindowVM; } }
}
4: On the page, bind the UserControl's new ItemsProperty DependencyProperty to the appropriate property of the page's viewmodel, again using RelativeSource binding:
<Window
...>
<local:FormControl
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type telerik:RadPane}},Path=viewModel.items}"
/>
</Window>

Resources