I'm using an ItemsControl that is populated with a collection containing several different data types. Each data type has its own DataTemplate, and they are all displaying correctly, but my problem is this: when there are several of the same type displayed one after another, the first one must display a header above it, and the last one must display a horizontal line below it. Is there a way to detect this in XAML, or do I need to pre-process this list a little to set flags?
The data is as follows:
Patient
Test
Result <-- header before this
Result
Result <-- line after this
Test
Result <-- header before, line after
Patient
Comment <-- header before this
Comment
Comment <-- line after this
Test
Result <-- header before, line after
I don't want to group the data types before displaying, they need to be displayed in the order in which they arrived.
You need to ask next/prevous sibling of each item about its type, and compare it with current item's type, and take into account that if both are equal and equal to the item, you do nothing... definitely you cannot do this with pure xaml.
Te easiest way to me would be to implement two bool properties in the viewmodel of the items 'HeaderVisible' and 'BottonLineVisible' and handle the logic there. Bind triggers to them in the DataTemplate to add top/bottom line as needed.
I think you would benefit from binding your ItemsControl to a resource key of a CollectionViewSource defined somewhere. You might be able to use the CollectionViewSource.GroupDescriptions[1] to show this in the UI.
Look at ItemsControl.GroupStyle[2] for how to style this.
[1] http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource.groupdescriptions.aspx
[2] http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.groupstyle.aspx
Related
I have an desktop WPF application that shows values from different files, the objective being to compare those files. The values extracted from the files are displayed in a DataGrid, one column per file. The user can add or remove files at will, which means that the amount of columns displayed in the grid can change. Columns are created and removed from a ViewModel, and have complex content that requires the use of DataGridTemplateColumn instances.
The data source is basically a two dimentionnal array (something like ObservableCollection<ObservableCollection<CellViewModel>>).
To fill my cells I need a data template, but the problem is that the DataTemplate's data source will be the row. And if I want to display the correct value, I'll have to know the index of the current cell from the template, so I access the correct index in my row. In the end, I'd like to be able to have a binding in the template that looks like {Binding Path=[someIndex].DisplayText} (DisplayText being a property of my CellViewModel).
I hope I've been clear enough :)
So the question is : how do I read the correct index in my row from the template ? Or, is there some better way to handle this variable-sized templated columns list problem ?
Thanks
In wpf with mvvm I use a ListBox with Canvas as ItemsPanelTemplate and DataTemplates to customize appearance.
I would like to improve performance by first adding all items of type 1 to the drawing, then all items of type 2.
I could create two ListBoxes both with Canvas as ItemsPanelTemplate and they would be overlays.
Panning and scrolling could be synchronized by means of bindings.
This way I can raise PropertyChanges for both lists independantly from each other.
Question: do you have experience whether overlaying canvases is good or bad for performance?
I an not sure whether this is also possible using a CompositeCollection for the ItemsSource of one ListBox. Or for that matter give both types a common subclass and stay with ObservableCollection.
Question: do you think that somehow a list with CompositeCollection can be given separate PropertyChanges for different parts of the Collection?
EDIT:
Suppose I have a great number of points, lines, labels for the canvas, each of a different type, however with a common base type. I select the DataTemplate using DataType: DataType="{x:Type my:Point}", DataType="{x:Type my:Line}" etc.
First as quicly as possible I want the user to see the lines. I raise PropertyChanged("Lines") and the ListBox+Canvas for the lines is visible.
In a backgroundworker I raise PropertyChanged("Points") and the ListBox2+Canvas2 for the points is visible.
When done in another backgroundworker I raise PropertyChanged("Labels") and the ListBox3+Canvas3 for the labels is visible.
There's a much simpler solution using basic Object Oriented Programming. Create a base data type and make all of your data objects extend from it. It could be empty, but if nothing else, you could implement INotifyPropertyChanged in it so you don't have to in each of the other data types.
Then you simply add a property of type ObservableCollection<BaseClass> to your view model or code behind and data bind that to the ListBox.ItemsSource property. As long as you don't set the x:Key values on the different DataTemplates, then WPF will implicitly set them to the relevant data type objects when it renders them.
So you can put all of your different data types in the same collection. Then you can add your first type in and wait for however long and add some of a different type, or whatever order you feel like.
UPDATE >>>
In response to the edit in your question, I don't quite understand the reason for you trying to use the INotifyPropertyChanged interface to show the items, type by type. If you have the base class collection that I mentioned above, then you simply add the instances of the first type to the collection first, so they'll appear first. When you want the next type to appear, just add them into the collection and then they'll appear and so on. The ObservableCollection will take care of that.
I have a DataTemplate(well two data templates) that I want to use as views for some
basic form viewmodels(that that contain a value and and boolean indicating whether I want to use the value).
I want to use the datatemplate(s) several times for separate form items. I think the right way to do this is to set it as the ContentControl's ContentTemplate (in that case it will have the same data context right?) but I also want to pass the label string and since the label string is part of the ui and doesn't change it seems wrong to put it in the viewmodel object. How do I give access of the label string to the DataTemplate instance?
Just like its name, a DataTemplate is used to template the Data... For example, if you have a class called MyItem which has a Name and Value and you want this shown in a specific way, you'll set a datatemplate for Item and use it whenever needed.
In your case, you're speaking about having very similar views, with only a minor change between them. This minor change (if I understood your question correctly) is not something that comes from the model or from the viewmodel but something which is entirely view-oriented (a different title for the page, for instance).
If you plan on using a different viewmodel for every view, and each viewmodel has a different purpose - I don't see a problem with adding a Title property to the VM and bind to that too (Remember, MVVM is a set of guidelines, not rules...)
If you still rather have it separated from the viewmodel, then you can use an Attached Property. Create an Attached Property called TemplateTitle, for instance, and have each contentcontrol in each view change it. The label, of course, will bind to that Attached Property.
Basic Question
What I am trying (and failing) to do is pretty simple conceptually. I have an ArrayList of brushes as a resource, and I would like to bind an individual element's color to an element at a specific index in the ArrayList. If possible, I would like to get the index to bind to from the element's datacontext.
What it would look like is something like this:
<Line Stroke={DynamicResource DefaultGraphBrushes[0]}/>
I have tried a number of permutations of this (explicitly setting the ResourceKey, using quotes, '.', etc), and I am hardcoding the index for now to see if I can even do this. I would ideally like to get the index from the datacontext of the line.
Context
I am trying to do this because I have a graph control that takes in an arbitrary number of data sets and plots them on a grid. Some of the data sets have colors defined in the view model that they should always be shown in, some do not. For the ones that do I am simply binding to the color they provide and that works fine. For the ones that do not I need to pick a unique color from a predefined list of colors. The application has several different "themes" available, and the list of colors to use for the graph is different for each theme in order to make it readable and pretty. The brush the line is using needs to update when the user changes themes.
In the view model for each data set it knows whether it has an assigned color or not, and if it does not it picks the next index in the list of predefined colors to use to prevent multiple data sets from using the same color (unless we have more data sets than colors, which is unlikely).
I have been searching for a way to do this for quite a while. Any suggestions on how to make this approach work, or on other approaches that may work better are greatly appreciated. Thanks!
sometimes i don't understand why one should try too long to find a Xaml solution if this can be done much easier in code... Why not Bind your Stroke to a property that computes the Color ? code might access as well to resources (with TryfindResource or SetResourceReference). if you use tryfindresource, NotifyPropertyChanged if resource changed.
I've done some digging in trying to solve this, but haven't yet found a solution that works for me.
Basically I have an <ItemsControl> and in the <ItemsControl.ItemsTemplate> I have a <ListView> which displays a number of people's names. There will always be between 0 and 5 people's names listed.
What I want to do is have a tooltip popup with additional information relevant to the entity that is being hovered over. How do I get the index (or the content like the name) of the item I am currently hovering over though, to ensure that what the tooltip is displaying is for the correct person?!
I have a MouseEnter event on the listview which is triggered every time the mouse moves over an entity & in debug mode I can explore down into the sender details & can find the person's name that I want, but how do I get to it from code?
What I want is something like:
int index = sender.GetCurrentlyHoveredOverItem();
I don't want to overcomplicate this post by listing everything I've tried, but if you want any further info, please let me know.
Thanks in advance for your help!
As others have hinted at, but not explicitly said, the items in the ListView should implement the tooltip directly, using an ItemTemplate if needed, rather than at a global level.
<ListViewItem ToolTipService.ToolTip="Tooltip for this item" />
So you have a collection (ItemsControl) of lists (ListView) and you need the tooltip to be specific to a specific element within one of the internal lists, did i understand that correctly?
If so, why not create a ListView.ItemTemplate to take care of that?
If you are using MVVM…
Bind IsMouseOver to a property in the VM, like "CurrentlyHoveredPersonList". Use OneWayToSource.
Create another VM property called "HoveredPersonListViewModel" that contains all the details you need for your tooltip. When CurrentlyHoveredPersonList gets set, populate HoveredPersonListViewModel and raise a property change notification. You'll get an actual reference to the object, so you might not need the index, but if you do you can use IndexOf(object) to get it from the source list right there in the view-model.
Bind the tooltip's DataContext to HoveredPersonListViewModel and its constituent controls to the appropriate properties thereof.