Is there an ItemsControl equivalent for text content? - wpf

I have some data that I want to present in a FlowDocument. This will basically be a view that explains the data in a friendly way, with section headers, paragraphs of text, etc., and which I will display in a FlowDocumentScrollViewer.
To this end, I would like to create a bulleted list (<List>) out of the contents of an ObservableCollection. I know how to do that with ItemsControl, but how do I do it for ListItem elements in a FlowDocument, since they're part of the TextElement class hierarchy rather than the Control hierarchy? Is there an equivalent of ItemsControl for text content inside a TextBlock or FlowDocument?
Edit: The article Sergey linked to is the perfect starting point. The only problem is that the article's code can only use a Section or a TableRowGroup as the items panel, and doesn't yet support using a <List>. But that was trivial to fix -- just a matter of adding this code at the end of ItemsContent.GenerateContent, just before the final else:
else if (panel is List)
((List) panel).ListItems.Add((ListItem) element);

What you are looking for is possible, but requires significant amount of coding. Fortunately, Vincent Van Den Berghe posted a nice article on the MSDN describing how to Create Flexible UIs With Flow Documents And Data Binding , including the code!

Instead of using a FlowDocument, you can use an ItemsControl and change the panel used to display items to a WrapPanel. This will allow you use the ItemsControl as you want, but change its display semantics to a WrapPanel (which I believe functions like a FlowDocument. You'd do it something like this:
<ItemsControl>
<ItemsControl.ItemsPanelTemplate>
<WrapPanel />
</ItemsControl.ItemsPanelTemplate>
</ItemsControl>
You can set any properties on the inner WrapPanel as you desire.

I think you are looking for the List element:
http://msdn.microsoft.com/en-us/library/system.windows.documents.list.aspx
Bubblewrap points out a few more details. You'd likely bind to the ListItems property and need to use a ValueConverter to convert your source list to a list of type ListItemsCollection.
Bubblewrap points out that this is readonly and that the ListItemsCollection has an internal constructor. So...
I think what you'd have to do is this:
<ContentControl Content="{Binding TheArrayOfText, Converter={StaticResource StringsToListConverter}" />
This is pretty unfortunate, but I think it would work. You'd have to write a converter to create a new List object and call .Add( on each item.

Related

How to make an ItemsControl that has only the special type?

My English skills are poor because I'm not a native English speaker.
I hope that you can understand.
I would like to construct a control that can receive only a special type.
I am thinking of constructing it deriving ItemsControl.
First, I tried to construct it deriving Panel but I thought that the way is unsuitable at this subject so I tried to use the ItemControl.
Anyway, here my problem is the ItemCollection of the ItemsControl can has all types but I want the control I try to construct to have only a special type.
For example, The construction of the code I expect is as follows.
(The name of the control that I'm trying to construct assumed "DockManager".)
(The name of the special type that "DockManager" can have, assumed "DockLayout" and "DocumentLayout".)
A successful example.
<DockManager>
<DockLayout Header="example" Content="{Binding}" DockManager.Dock="Top"/>
<DockLayout Header="example2" Content="{Binding}" DockManager.Dock="Bottom"/>
<DocumentLayout Header="example2" Content="{Binding}"/>
</DockManager>
Example of failure
<DockManager>
<Grid/> <-- Error : DockManager can't has the Grid control.
<TextBox/> <-- Error : DockManager can't has the TextBox control.
</DockManager>
Could I reach the above goal in the base on ItemsControl?
I am not willing to cling to ItemsControl.
If you have a better way that can reach the above goal, could you teach me the way?
Thank you for reading.
Could I reach the above goal in the base on ItemsControl?
Short answer: No.
An ItemsControl has an ItemsSource property that can be set to any IEnumerable and you can't change this API by creating a new class that derives from it.
So if you want a control that only works with a spcific type of items, you will have to create your own custom control from scratch basically. You may take a look at the source code of the ItemsControl and proceed from that.

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 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.

Creating instances of resources?

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.

Different item template for each item in a WPF List?

I have many items inside a list control. I want each item to have a different item template depending on the type of the item. So the first item in the list is a ObjectA type and so I want it to be rendered with ItemTemplateA. Second item is a ObjectB type and so I want it to have ItemTemplateB for rendering. At the moment I can only use the ItemTemplate setting to define one template for them all. Any way to achieve this?
the ItemTemplateSelector will work but I think it is easier to create multiple DataTemplates in your resource section and then just giving each one a DataType. This will automatically then use this DataTemplate if the items generator detects the matching data type?
<DataTemplate DataType={x:Type local:ObjectA}>
...
</DataTemplate>
Also make sure that you have no x:Key set for the DataTemplate.
Read more about this approach here
Have a look at the ItemTemplateSelector property of your list control. You can point it to a custom TemplateSelector and decide which template to use in code.
Here's a blog post describing TemplateSelectors:
http://blogs.interknowlogy.com/johnbowen/archive/2007/06/21/20463.aspx
Edit: Here's a better post:
http://blog.paranoidferret.com/index.php/2008/07/16/wpf-tutorial-how-to-use-a-datatemplateselector/

Resources