Silverlight 3 - Find Element inside items control - silverlight

I have an ItemsControl that is bound to a collection of objects. Each object has it's own collection as well as other vital properties. To display the objects within an object, I'm displaying a TreeView inside of a ItemsControl. I know this sounds crazy. But, this is just a trimmed down version of what I am trying to accomplish to keep the question focused on the problem. Here is my sample:
<ItemsControl x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:TreeView x:Name="myTreeView">
</controls:TreeView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When a user clicks a button, I need to retrieve the current TreeView associated with a specific object. In an attempt to do this, I am trying the following:
MyClass instanceToFind = (MyClass)(IdentifyDesiredInstance());
foreach (MyClass instance in myItemsControl.Items)
{
if (instance.ID == instanceToFind.ID)
{
TreeView treeView = null; // How do I get the TreeView?
// Do other necessary updates
}
}
The code snippet above shows where I am trying to get the TreeView. How do I get the TreeView when looping through the items in an itemscontrol?
Thank you!

You need to use the VisualTreeHelper.GetChild and VisualTreeHelper.GetChildrenCount methods to iterate through the view's children until you find the tree that corresponds to your item. You should be able to check the TreeView.DataContext property against your item to verify its the right one. Note, you need to use this recursively as GetChild only retrieves immediate children.
As you'd need to iterate the visual tree anyway, I recommend abandoning your current loop and instead, just looping the children, checking the ID of their data contexts.

Related

ItemsControl with databound ItemsSource and two-way binding of elements (replacable items)

I have an ObservableCollection of objects (e.g. Persons with First/Last Name) which I would like to display in an ItemsControl. Each Item is displayed in a custom "editor" control, which allows editing of the object's properties.
This part is working fine and fairly standard.
<ItemsControl ItemsSource="{Binding Persons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<custom:PersonEditor Person="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
However, the custom editor control also has the ability to replace the entire object is has received (rather than just editing a person's name, replace it with a new person object).
What I am looking for is a way to push this change back into the ObservableCollection. As it is now, changing the Person object within the editor does not replace the item in the list, which would be the desired outcome.
Any help would be appreciated.
If you don't have access to the custom control itself what you could try would be using a setter on a property to clear the ObservableCollection and re-add the items instead of outright replacing it.
For example:
private ObservableCollection<MyModel> _dataSource;
public ObservableCollection<MyModel> DataSource
{
get
{
return _dataSource;
}
set
{
_dataSource.Clear();
foreach(var item in value)
{
_dataSource.Add(item);
}
}
}
This would prevent the item itself from getting replaced which causes issues with ItemSources since they apparently only bind to the items property once.

WPF: How to bind to only one item in a collection, not using ItemsControl since I don't want to display all of them

I have this requirement, that I have a collection of items (ObservableCollection), but I only want to display the first item. The requirement comes from the fact that in most of the case, the collection only contains one item. And due to the space limit, even if there is more than one items in the collection, we'd like to display the number of the items, details of the first one (same presentation as prior situation) and a ... symbol to indicate to the user that there is more items. And when the mouse is over the UI element a popup will eventually display all items.
The first solution I can think of (please suggest others if they are better) is to bind to this collection (but not using an ItemsControl) and define a DataTemplateSelector derived class (which return either the DataTemplate to display the only one item, or the DateTemplate which has the ... and the popup for more details, based on the number of items in the collection) and use it as ContentTemplateSelector.
But now my question: how both of my DataTemplate would look like in XAML, so that they can display only the first item in the collection? Obviously I can't have a ItemsControl.
UPDATE:
Now I have managed to make it work and agree this question can be closed (I can't delete it anymore since there is already some answers).
I actually knew how to bind to one certain item in the collection, but this was not where I am confused. I felt I should use ContentControl as one answer suggests. But I thought since I need to bind to the whole collection (not to single indexed item), and use a DataTemplateSelector to select the proper DataTemplate based on the number of items in the collection. The code would look like this:
<ContentControl Content="{Binding MyCollection}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
And in MyTemplateSelector I wasn't sure how to use it since there is no reference to my collection because it is defined as resource and it doesn't have the information of MyCollection. However, it turned out to be very simple, the DataTemplate can refer to an indexed item without knowing the name or any other reference. Simply like this:
<DataTemplate>
<TextBlock Text="{Binding [0].PropertyName}" />
<DataTemplate />
To bind to just one item from a collection, you can use the following syntax:
{Binding Items[0]}
Or to bind to a property of a single item from the collection:
{Binding Items[0].Property}
You can find out more about property path syntax from the Binding.Path Property page at MSDN... from the linked page:
• Indexers of a property can be specified within square brackets following the property name where the indexer is applied. For instance, the clause Path=ShoppingCart[0] sets the binding to the index that corresponds to how your property's internal indexing handles the literal string "0". Multiple indexers are also supported.
Try this
<ContentControl Content="{Binding YourCollection[0]}">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
Ok, late to the party but I thought I'd share my 2 cents anyway: I'd better go with a dumber (XAML-)view and a view-model closer to your presentation needs.
Translated: instead of mapping your existing view-model (or raw data) and its collection of items directly to the view, I suggest to map that to an appropriate view-model showing something like a YourItemViewModel FirstItem property and a bool HasMore property. That second view-model would be easily unit-testable to make sure it behaves propertly, and would be easily mapped to a view with less logic, so to avoid possible hard-to-test problems in view.
{Binding Items[0].SomeProperty}
{Binding [0].SomeProperty}
{Path=/SomeProperty}

WPF Treeview with HierarchicaldataTemplate - don't show lowest item

I have a WPF TreeView with a HierarchicalDataTemplate. As I descend the hierarchy, expanding the nodes, I will eventually get to the bottom and data displayed via a normal DataTemplate.
I'd like not to show those nodes - if I set the DataTemplate's containing TextBlock to Visible Hidden (or similar) I just get allocated space in the treeview. I'd like not to display those items so assume I need to remove them somehow. I cannot use a Filter on a CollectionView as there may be other nodes with children at this level. So basically, at any level I want to remove those nodes that have no children. The actual data is being loaded from an Xml file via an XmlDataProvider, so there are no class objects.
Can anyone suggest how
thanks
John
Presumably you ask the question because the last nodes in the tree are of the same type so you use only the one HierarchicalDataTemplate:
<HierarchicalDataTemplate DataType="{x:Type src:MyNodeClass}" ItemsSource = "{Binding Path=Items}">
<TextBlock Text="{Binding Path=PropertyToDisplay}"/>
</HierarchicalDataTemplate>
If you could change the type of your last nodes they will not automatically use the template (you could also inherit from the normal type so the collection allows them even though the new class is actually empty).

How to pass bound data item to a ListBox item's ViewModel?

Trying to solve a very simple problem using mvvm-light, but after days of sifting through StackOverflow and lots of Google searches, I have not come up with a simple solution.
I have a ListBox with a dataTemplate. The dataTemplate contains one userControl to display the content
<ListBox ItemSource={Binding Posts} >
<ListBox.ItemTemplate>
<DataTemplate>
<ctl:PostControl/> <-- child control I'm trying to pass data to
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I've got viewModels on both the parent page (used to bind to the posts, no problem there) and on the PostControl to display the individual posts.
Question: How do I get the individual post (from the binding of the posts control) into the viewModel of the PostControl?
I used a DataContext on the PostControl definition:
DataContext="{Binding PostControlViewModel, Source={StaticResource Locator}}"
which seems to work, but I need access to the individual Post bound to this control by the parent ListBox. How can I pass the individual post into the PostControls' viewmodels?
Without seeing your UserCtl it's hard to say what goes wrong, but I would say your ListBox looks OK, and each Control should be bound to an element of Posts.
What you should not do is override that in the UserCtl, so I think that DataContext="..." attribute should simply go.
Assuming that Posts is a list of PostControlViewModel that is. If it is a list of the (Business) Model Post class, you need a converter. But it should contain ViewModels.

WPF contextmenu and ListView

Ok, hopefully this is simple but for some reason I can't find a straight answer and I'm not familiar enough with WPF yet to know how to do it.
I have a listview, it gets bound to an observable collection of objects to display. I want to have a context menu with a bunch of options. The options in the context menu are relative to the particular object in the list that was clicked on (things like delete, export, etc).
So I need the object that the user right clicked on in my listview to be passed as a parameter to the command that the context menu executes.
How do I do this?
Edit: I should mention I would prefer a solution that is mostly (if not entirely) xaml - I'm trying to avoid having significant code in the code-behind. If that's the only way to do it though...
Further Edit: More details that I forgot to mention that are important. The command I want executed is on the object bound to the data context of my user control, it is not on the objects in the list view. So I need the context menu's on the list view's items to be bound to a command that is on the user control's data context, and the listview item passed as a parameter into that command.
It depends on whether your ContextMenu is part of the template for individual items, or if it is attached to the ListBox as a whole.
If you are attaching your ContextMenu to the items in the list using a DataTemplate (this is generally the best way to do it), the DataContext on the MenuItem is already set so all you need to do is:
<MenuItem ... CommandParameter="{Binding}" />
On the other hand, if your ContextMenu is attached to the ListBox as a whole, you'll need to access the SelectedItem property of the ListBox:
<MenuItem ... CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor,ListBox,1}} />

Resources