How to find and delete Binding from dynamically added and deleted UserControl - silverlight

I have a set of UserControl, which share the same business object (ViewModel), but only
display data from this in different way.
I select an active UserControl via combobox. Old UserControl I delete from StackPanel and add new UserControl.
var uiElement = thisObject.EditorsContainer.Children.FirstOrDefault();
if (uiElement != null)
{
thisObject.EditorsContainer.Children.Remove(uiElement);
uiElement.Cast<UserControl>().ClearValue(DataContextProperty);
}
EditorsContainer is the StackPanel
It looks like a parent control keeps reference on the deleted control, because when I edit value in active control, the binding to binded property updates deleted control in memory.

As workaround I have implemented an interface IActiveAware with IsActive property, which is false when the logical life of user control has ended. On the bound property in ViewModel I check this property.

Related

Binding to ViewModels that are displayed using a ContentPresenter and DataTemplates?

I am currently using an ItemsControl at the top of my application that contains a few buttons, bound to a list of ViewModelBases. These VMs are the pages of my app. When clicked, they set the current page to the appropriate page. A content presenter shows the current view model, and a DataTemplate converts it into the correct view.
How can I use Data Binding in these views? Since the view is being created by the ViewModel, doesn't that mean I can't create a new instance of that view model in the view? How can it find the correct instance to bind to? Or, is it automatically bound?
WPF ItemsControls automatically set the DataContext of each Item they render on screen to their corrsponding Item in the underlying collection. This means that, if you have an IEnumerable set as the ItemsSource of an ItemsControl (or any other ItemsControl-derived class, such as ListBox), it will render every item on screen (using any available DataTemplates) and automatically assign the DataContext property of the resulting visual elements to each of the ViewModelBase Items.

TabControl - databinding TabItem order

I've got a datababound TabControl and would like to bind the index of each TabItem to a corresponding property in my view model. The ItemsSource is an ObservableCollection, and I'm using Bea Stollnitz's Drag/Drop functionality to provide tab control re-ordering.
My gut feeling is that it should be able to be handled in the data template for the tab item header, but I haven't been able to get it working.
Your TabControl.ItemsSource should be bound to your collection, so to re-arrange the order of tab items, simply re-arrange the collection.
I've worked with Bea's drag/drop code before to create a TabControl that allowed users to drag/drop the tab items, and I think most of what was needed is in the code she provides. On drop, it removes the dragged object from it's parent collection, and inserts it to its new location in the drop target collection, which in your case is the same collection.
Edit
Based on your comment below about updating your ViewModel with the Tab Index, try using the CollectionChanged event.
void MyCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
foreach (var item in MyCollection)
item.TabIndex = MyCollection.IndexOf(item);
}

"Nested" MVVM Question

I have a WPF window that has a datagrid and a user control for a form for the fields in that datagrid. The user control and the WPF window have view models.
The user control's DataContext is bound to one of the window's view model's member field, whose value changes during the data grid's Selection Changed event.
I'm not sure if this is the right way to do it because I am unable to create references from the inner view model to the outer view model for some reason. Constructor injection won't work because I'm required to use default constructor only, and I can't seem to put a property injector in the right place (always getting null reference when I try to use it).
I am also unable to get my property change notification to work properly in the inner view model.
Is there a better way to wire my view models so that they automatically change the values in the user control when a new row is selected in the datagrid? I have a feeling that binding to the control's DataContext is not the way to go.
This doesn't seems a complex/nested scenario. Looks like pretty much a normal master details scenario. Suppose you want to edit Customer data, I would have an ObservableCollection instance bind to the DataGrid, and there will be a SelectedCustomer property in the VM also. In the DataGrid you can set SelectedItem twoway bind to the SelectedCustomer property which makes SelectedCustomer always updated with your selection. Since the usercontrol has the same instance of customer as in the DataGrid row, whenever you change anything in the UC those data will get reflected in the grid. Ofcourse all those properties should fire NotifypropertyChanged.

WPF Accessing Items inside Listbox which has a UserControl as ItemTemplate

I have Window that has a ListBox
ListBox(MyListBox) has a DataTable for its DataContext
ListBox's ItemSource is : {Binding}
Listbox has a UserControl(MyUserControl) as DataTemplate
UserControl has RadioButtons and TextBoxes (At first They're filled with values from DataTable and then user can change them)
Window has one Submit Button
What I want to do is, when user clicks the submit button
For each ListBox Item, get the values form UserControl's TextBoxes and RadioButtons.
I was using that method for this job :
foreach(var element in MyListBox.Items)
{
var border = MyListBox.ItemContainerGenerator.ContainerFromItem(element)as FrameworkElement;
MyUserControl currentControl = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(myBorder,0) as Border,0)as ContentPresenter,0)as MyUserControl;
//And use currentControl
}
I realised nothing when using 3-5 items in Listbox. But when I used much more items, I saw that "var border" gets "null" after some elements looped in foreach function.
I found the reason here :
ListView.ItemContainerGenerator.ContainerFromItem(item) return null after 20 items
So what can I do now? I want to access all items and get their values sitting on user controls.
Thanks
You should use objects who implement INotifyPropertyChanged and bind an ObservableCollection of it to the ItemSource
And then you can get all the list of items.
Here some quick links from MSDN to get more informations
How to: Implement Property Change Notification
Binding Sources Overview
You should google for some tutorials about this.
Zied's post is a solution for this problem. But I did the following for my project:
I realised that there's no need to use UserControl as DataTemplate in my project. So I removed ListBox's DataTemplate.
I removed MyListBox.DataContext = myDataTable and used this:
foreach(DataRow dr in myDataTable.Rows)
{
MyUserControl muc = new MyUserControl(dr);
myListBox.Items.Add(muc);
}
I took DataRow in my UserControl's constructor and did what I want.
And at last I could access my UserControls in ListBox by using :
foreach(MyUserControl muc in
myListBox)
{
//do what you want
}
Easy huh? :)

WPF ListBox - Getting UIElement instead of of SelectedItem

I created a ListBox that has a DataTemplate as Itemtemplate. However, is there an easy way to access the generated UIElement instead of the SelectedItem in codebehind?
When I access SelectedItem, I just get the selected object from my
ItemsSource collection. Is there a way to access the UIElement (ie. the
element generated from the DataTemplate together with the bound object)?
You are looking for the ItemContainerGenerator property. Each ItemsSource has an ItemContainerGenerator instance. This class has the following method that might interest you: ContainerFromItem(object instance).
Once you have a handle to the ListBoxItem, you can go ahead and browse the logical and visual tree. Check out Logical Tree Helper and Visual Tree Helper.
Like Andy said in the comments, just because the item exists in your collection doesn't mean a container has been generated for it. Any kind of virtualizing panel scenario will raise this issue; UIElements will be reused across the different items. Be careful with that as well.
siz, Andy and Bodeaker are absolutely right.
Here is how I was able to retrieve the textbox of the listbox's selected item using its handle.
var container = listboxSaveList.ItemContainerGenerator.ContainerFromItem(listboxSaveList.SelectedItem) as FrameworkElement;
if (container != null)
{
ContentPresenter queueListBoxItemCP = VisualTreeWalker.FindVisualChild<ContentPresenter>(container);
if (queueListBoxItemCP == null)
return;
DataTemplate dataTemplate = queueListBoxItemCP.ContentTemplate;
TextBox tbxTitle = (TextBox)dataTemplate.FindName("tbxTitle", queueListBoxItemCP);
tbxTitle.Focus();
}
(Note: Here, VisualTreeWalker is my own wrapper over VisualTreeHelper with various useful functions exposed)

Resources