Creating instances of resources? - wpf

I'm brand spanking new to WPF and am trying to play around with projects to better understand what I'm reading.
My understanding of a resource is that it is the instance, you can't use it like a factory and create instances of it. For example, a XAML-defined rectangle. You can reference it, but you can't have numerous instances of it all over the surface.
In WPF, what would be the way to do that? If I define a Rectangle as a resource with specific properties and wanted to have multiple instances of that within a dynamically-generated grid, how should I be going about it? Or is there a different way I should be trying to do this?
Purely academic exercise with no real-world application.

Actually there's nothing about resources in particular that prevents you from using it multiple times. A perfect example of this is brush resources, style resources, etc. You define them in XAML and the XAML parser creates a single instance of the resources and stores them in the resource dictionary and these brushes, styles, etc can be used as property values many times even though only a single instance of the resource was created.
But having said that, as you noted, you can't really define a Rectangle resource and use it multiple times in the visual tree. This has nothing to do with the fact that it's a resource, but rather it has to do with the fact that a FrameworkElement cannot be a child of more than one parent element.
So what we have instead is called "templates". These tell WPF how to create an element tree but does not actually create the tree until you instantiate the template. Below is an example.
<UserControl>
<ItemsControl ItemsSource="{Binding WholeBunchOfItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Fill="Yellow" />
<ContentPresenter Content="{Binding}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
In this example I've bound an ItemsControl to a collection of some sort. For each item in the collection, the ItemsControl will use my DataTemplate to render the item. Within a DataTemplate you can use data binding to access the current item.
I would suggest reading up on MSDN about ControlTemplate, DataTemplate, and Style. These are all important concepts in WPF/Silverlight.

To get multiple instances replicated across a grid or listbox, you need to set the data template to define the UI controls for each row of data, and then databind the grid or listbox to a collection of data that determines how many rows and the individual field values.
Key term for you to research first: data template.

Related

WPF TreeView question

Is it possible to store some data in every item of a TreeView control? I mean, something useful (e.g. a string) besides the header text?
Thanks.
Yes, WPF is "lookless", so your actual data can be anything you want it to be, and a TreeView is just a Template used to display the data to the user in a pre-determined way.
You can overwrite any part of that Template to be whatever you want, and/or have it bind to your data however you want.
Edit
I'm no expert on using the TreeView, but if you had a DataContext of List<Folder>, and each Folder object had a Name and a FullPath property, your TreeView could look something like this:
<TreeView ItemsSource="{Binding MyFolderList}">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
ToolTip="{Binding FullPath}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
If you haven't already, I'd highly recommend looking into the MVVM design pattern when working with WPF. Basically your application is your classes (ViewModels), and the Controls/XAML (Views) are just a pretty layer that sits on top of your classes to make them user-friendly.
This is an important concept when switching from a WinForms TreeView to a WPF TreeView
It depends on what you mean by store data...
If you're just talking UI customization Rachel's answer above works.
If you're talking about storing arbitrary object values, such as information about the TreeViewItem, or maybe a relation between two items, you can use the Tag property of TreeViewItem. For example, I had to write a mapping UI where two trees linked together where each TreeViewItem from the first tree, could connect to 1 TreeViewItems of the second tree. I used the Tag property of the first TreeViewItem to store the connecting TreeViewItem.

how can I bind Bing Pushpins from multiple models?

I have the requirement in my WP7 application to display pushpins on a bing map from multiple data sources and I am not sure of the best way to do this.
So for example it would be something like this, I receive from a web service a list of people, a list of buildings, a list of POIs etc. I would need to display these individually in their own views but also display them on a map with different images for each type.
I am trying to use a MVVM approach so have a class for a Person, class for a building and so on, each one of these has a location. I then have an ObservableCollection for each of these types and so using data binding it is easy enough to do a View for each of these.
At the moment I only have one ViewModel but my first thought is that I think I should really have one ViewModel per type. So PersonViewModel, BuildingViewModel here? However a Map View would then need to take information from each of these views and I am no sure how you bind a view to multiple ViewModels like this or even if that is sensible.
MapItemsControl also only seems to be able to bind to one thing so how can I bind it to multiple different data sources like this? I thought I could create a simple pushpin class but that would then mean having the data for each type duplicated and I eventually want to be able to click on the pushpins to display the details for the pin so wanted to keep the types separate
Any pointers on a way forward much appreciated
Using a MapItemsControl is as you say, the way to do it. And it's very easy. What you want is a ViewModel with a ObservableCollection for each of your types, as you say you already have.
So I guess your problem is that you don't understand how to use Styles and ItemTemplates.
If what you want, is to have the same style for each of the different types, then simply define a common ItemTemplate that you apply to each MapItemsControl.ItemTemplate. Likewise you can define a custom style that you apply to each Pushpin globally, if what's what you want.
<Grid.Resources>
<DataTemplate x:Name="PushpinItemTemplate">
<maps:Pushpin Location="{Binding Location}" Tap="Pushpin_Tap" Style="{StaticResource PushpinStyle}" />
</DataTemplate>
</Grid.Resources>
...
<maps:Map>
<maps:MapLayer>
<maps:MapItemsControl ItemsSource="{Binding People}" ItemTemplate="{StaticResource PushpinItemTemplate}" />
<maps:MapItemsControl ItemsSource="{Binding Buildings}" ItemTemplate="{StaticResource PushpinItemTemplate}" />
</maps:MapLayer>
</maps:Map>

How to present a collection of objects in a listbox in WPF

I have a collection of objects that I want to present. How can I do this? A listbox would do but each object has many attributes which I want to present. I already bound a listbox to a collection and I have all my objects listed. My question is regarding the visualization of the listbox and if the listbox is the correct thing to use or there is something else that I should use.
I find the existing answers to be a bit lacking, normally one would not process collections or boil their items down to a string, at the very best you would do some dynamic manipulation using a CollectionView (e.g. sorting, grouping), but normally you use Data Templating to display the individual items which allows you to use all their properties.
Further there are several controls which work well with collections, firstly you need to know if you want selection, if not an ItemsControl is a good choice, otherwise you should use a ListBox or ListView.
ListViews are normally employed if you have different views for your objects, e.g. a details view and a thumbnail view. You can use the ListView.View for this, there is one existing view in the framework, the GridView, which offers columns. What Matthew Ferreira suggested is exactly what you should not do with a ListView since you want to make the templates dependent on the current view, in fact that code does not even compile since DataTemplate can only have one child.
ListViews are supposed to encapsulate the view logic in their view so it can be changed at will. If you decide to use a ItemsControl or ListBox then setting the ItemTemplate is what you want to do. Read the Data Templating overview i linked to, it makes for a good starting point.
You might want to consider using a ListView control instead. ListView has support for columns if you are planning on showing several properties from your object. You can use the ItemTemplate property to format the display of your object. For example:
<ListView ItemsSource="{Binding Path=myObjectCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}"/>
<CheckBox IsChecked="{Binding Path=ShouldCheck}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This example assumes that your object has the properties Title and ShouldCheck.
Your collection of object is probably to be viewed as your model. The usual thing in WPF is to add a ViewModel that translates and exposes the model data into a form suitable for binding. Depending on what you want to do, your VM could e.g. format each object into a string representation and then expose it as a collection of strings that the Listbox can bind to and display.

How do you navigate a complex Visual Tree in order to re-bind an existing element?

In the above image, child is a ContentPresenter. Its Content is a ViewModel. However, its ContentTemplate is null.
In my XAML, I have a TabControl with the following structure:
<local:SuperTabControlEx DataContext="{Binding WorkSpaceListViewModel}"
x:Name="superTabControl1" CloseButtonVisibility="Visible" TabStyle="OneNote2007" ClipToBounds="False" ContentInnerBorderBrush="Red" FontSize="24" >
<local:SuperTabControlEx.ItemsSource>
<Binding Path="WorkSpaceViewModels" />
</local:SuperTabControlEx.ItemsSource>
<TabControl.Template>
<ControlTemplate
TargetType="TabControl">
<DockPanel>
<TabPanel
DockPanel.Dock="Top"
IsItemsHost="True" />
<Grid
DockPanel.Dock="Bottom"
x:Name="PART_ItemsHolder" />
</DockPanel>
<!-- no content presenter -->
</ControlTemplate>
</TabControl.Template>
<TabControl.Resources>
<DataTemplate DataType="{x:Type vm:WorkSpaceViewModel}">
....
WorkSpaceViewModels is an ObservableCollection of WorkSpaceViewModel. This code uses the code and technique from Keeping the WPF Tab Control from destroying its children.
The correct DataTemplate - shown above in the TabControl.Resource - appears to be rendering my ViewModel for two Tabs.
However, my basic question is, how is my view getting hooked up to my WorkSpaceViewModel, yet, the ContentTemplate on the ContentPresenter is null? My requirement is to access a visual component from the ViewModel because a setting for the view is becoming unbound from its property in the ViewModel upon certain user actions, and I need to rebind it.
The DataTemplate is "implicitly" defined. The ContentPresenter will first use it's ContentTemplate/Selector, if any is defined. If not, then it will search for a DataTemplate resource without an explicit x:Key and whose DataType matches the type of it's Content.
This is discussed here and here.
The View Model shouldn't really know about it's associated View. It sounds like there is something wrong with your Bindings, as in general you should not have to "rebind" them. Either way, an attached behavior would be a good way to accomplish that.
I think the full answer to this question entails DrWPF's full series ItemsControl: A to Z. However, I believe the gist lies in where the visual elements get stored when a DataTemplate is "inflated" to display the data item it has been linked to by the framework.
In the section Introduction to Control Templates of "ItemsControl: 'L' is for Lookless", DrWPF explains that "We’ve already learned that a DataTemplate is used to declare the visual representation of a data item that appears within an application’s logical tree. In ‘P’ is for Panel, we learned that an ItemsPanelTemplate is used to declare the items host used within an ItemsControl."
For my issue, I still have not successfully navigated the visual tree in order to get a reference to my splitter item. This is my best attempt so far:
// w1 is a Window
SuperTabControlEx stc = w1.FindName("superTabControl1") as SuperTabControlEx;
//SuperTabItem sti = (SuperTabItem)(stc.ItemContainerGenerator.ContainerFromItem(stc.Items.CurrentItem));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(stc);
//ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(sti);
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
The above code is an attempt to implement the techniques shown on the msdn web site. However, when I apply it to my code, everything looks good, except myDataTemplate comes back null. As you can see, I attempted the same technique on SuperTabControlEx and SuperTabItem, derived from TabControl and TabItem, respectively. As described in my original post, and evident in the XAML snippet, the SuperTabControlEx also implements code from Keeping the WPF Tab Control from destroying its children.
At this point, perhaps more than anything else, I think this is an exercise in navigating the Visual Tree. I am going to modify the title of the question to reflect my new conceptions of the issue.

WPF : Multiple views, one DataContext

I'm working on a WPF application which must handle multiple screens (two at this this time).
One view can be opened on several screens and user actions must be reflected consistently on all screens.
To achieve this, for a given type of view, a single DataContext is instantiated. Then, when a view is displayed on a screen, the unique DataContext is attached to it. So, one DataContext, several views (same type of view/xaml).
So far so good. It works quite well in most cases.
I do have a problem with a specific view which relies on ItemsControl. These ItemsControl are used to display UIElements dynamically build in the ViewModel/DataContext (C# code). These UIElements are mostly Path objects. Example :
<ItemsControl ItemsSource="{Binding WindVectors}">
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Canvas IsItemsHost="True" />
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
Here, WindVectors is a ObservableCollection<UIElement>.
When the view is opened the first time, everything is fine. The problem is that when the view is opened one another screen, all ItemsControl are removed from the first screen and displayed one the second screen. Other WPF components (TextBlock for instance) on this view react normally and are displayed on both screens.
Any help would be greatly appreciated.
Thanks.
Fabrice
This is the expected behavior (ie been that way since winforms)- this is because the ObservableCollection is a reference. This wont happen with value types, only reference types.
The short answer is 'dont do that'. You could try looking into defining a collection view in the xaml or code a custom data provider and bind to one of those instead.

Resources