Can you insert items between ListBoxItems in a ListBox? - wpf

Simple question. We want a divider to appear between each item in a horizontally styled listbox. Think the arrows between entries in a crumbtrail. However, we don't want that divider to be part of the selection, so it really should go between each ListBoxItem. Is it possible to do that?
The only two ways I can think to do this is to insert 'separator' items into the data and template them differently, but that requires mucking with the items collection, which you may not be able to do in a data bound situation.
The only other way would be to style the actual ListBoxItem to have a separator outside of the highlighting border, then template the last one in the list separately.
Can anyone think of any other way to do this?

Interesting question!
As you said, one way would be to mock the ItemsSource collection.
Such as:
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource mockConverter}">
<Binding Path="YourCustomList" />
</MultiBinding>
</ListBox.ItemsSource>
Note that I used multibinding with only one binding. I believe that is the correct way in order for the converter to be "re-run". This way, when you modify items in "YourCustomList", it will retrigger the converter.
Unlike this, which will trigger converter only once(when ItemsSource is first binded against)
<ListBox ItemsSource="{Binding YourCUstomList, Converter={StaticResource mockCOnverter}}" />
^ wont work if you add new items.(Converter is only re-run when YourCustomList property changes)
MockConverter will be easy, just make sure you generate list with seperators. You need new class for a seperator. This way you can easily use DataTemplates(DataType). Next you probably have to set up a new trigger in ItemContainerStyle in order to set IsEnabled=false when object=Seperator.
It will be relatively painless and I see no complications. It's a little ugly because seperators should not be a ListBoxItems.
Perhaps this could also work.
You can override ListBox ItemsPanelTemplate with your own. In your own template, you can do whatever you want. Perhaps add seperators. This way, you wont touch ItemsSource.
Check out StackPanel.cs for code.
I can't provide with you a code right now, but the idea is this; you inherit from StackPanel, and overwrite Measure() and Arrange(). With those functions, you can calculate how big your stackPanel should be, and provide the location(X,y) where the seperators should be drawn at.
Note that the seperators have to be Children of StackPanel and they need to have IsHitTestVisible=False(so they wont generate events).
Later solution takes time, but if you are learning WPF, then why not?

DataTemplate is fine, I don't think you would have problems mocking the items collection. Why yould you ?
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Seperator/>
<TextBlock Text="{Binding .}"/>
</StackPanel/>
</DataTemplate>
I won't recommend the last option you had. That's just not right to have it separated style just to achieve what you want.

Related

How can I suppress a ListView from displaying when content when empty

I have an object that contains an IEnumerable called ValidPhones as a property. When I bind it to a WPF ListView like such:
<ListView DataContext="{Binding ValidPhones}">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat='{}{0}: {1}'>
<Binding Path='Type' />
<Binding Path='PhoneNumber' />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</ListView>
It still ends up displaying the ": " in the UI even when the Enumerable is empty. Is there a simple way to suppress that from happening?
What backend language are you using with wpf? I'm fairly sure it's either c# or VB.NET. I'm no expert, but I'm pretty sure you can access things like ListViews from code.
EDIT: sorry...I missed the comment at the top. Please mark your threads as closed when you solve the problem so silly people like me don't post useless answers.
Sure! In fact, there are a few options for you to consider.
Option 1: You can always change the visibility of your control to "collapsed" when the list is empty, which will keep your application from rendering it in the xaml. This can be done from your C# code or your xaml. this is a viable solution, but be careful that you don't have other controls that are dependant upon the location of the listview, because setting the visibility to "collapsed" will tell your applicaitonPage not to render the listview at all, which can mess with your layout.
Option 2: You can set up an "IsEmpty" attribute for your iEnumerable, and then bind that attribute to the textblock's text property. If you do it this way then you will need a converter that can return either a blank string or the properly formatted one depending on the value of IEnumerable. This opiton is slightly more complicated, but it has the benefit of leaving your listview alone so that the other controls don't move around.
Please let me know if you need help with either of these options, or if they need clarificaiton :)

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.

SelectedItem of SelectedItem

first of all I would like to thank you for the many good posts that i read in this forum. Unluckily I could not find anything of help for my current problem (either here or anywhere else).
What I'm trying to do sounds quite simple, but I have no clue how to get it to work ... perhaps I'm still to new to wpf or I don't thing wpfy enough :)
I'm designing a front end for a part in an automated manufacturing:
I have a quantity of places where pallets can be put (but it can be empty as well).
Each pallet has up to 3 places where parts can be mounted
Everything is created dynamically of a database and is reacting to changes.
The position of the parts on the pallet comes from the database as well and should be visualized
What I would like to have is:
An overview over the pallet-places with a preview of the pallet
When I select a place I want to see a detail view of the place
When I click on a part on the pallet of the detailed pallet I want to see details to the part
The first two points are quite simple and work nicely:
I have got a DataTemplate for every component (part, pallet, pallet-place). Actually those are UserControls that are imported as Datatemplates
the overview is a ListBox with the places as DataContext
for the detail-place-view I use the UserControl and bound it to the SelectedItem of the Listbox
I tried to bind the Text of a Textblock to the ID of the selected Part ... and fail.
Probably I could use some global variables in the code behind - but that sound very ugly.
Can anybody help?
I have got a solution ... it is not nice but works.
I created an event in the pallet, that triggers, when the selected part-place changes
I handle the event in the pallet-place and create a new one
And finally I handle it in the overview and change the detailview accordingly
Most likely there are much nicer solutions, but it will suffice.
Perhaps try an ElementName binding?
<TextBlock Text="{Binding ElementName=Name_of_your_Listbox, Path=SelectedItem.ID" />
Can you post a bit more code of your TextBlock and your Binding?
Context is important, if i use a ContentControl and bind its content to the SelectedItem like this:
<ContentControl Content="{Binding SelectedItem, ElementName=mylistbox}">
I can bind to the ID of the selected item in the DataTemplate like this:
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</ContentControl.ContentTemplate>
That is because setting the Content of the ContentControl automatically sets the DataContext as well, and this binding is relative to the DataContext since no source (ElementName, RelativeSource, Source) has been specified.
I do not know how your UserControl handles the context, if the DataContext is not affected such bindings will not work. You would need to bind directly then:
<uc:MyDetailsView Data="{Binding SelectedItem, ElementName=mylistbox}">
<!-- ... -->
<TextBlock Text="{Binding SelectedItem.ID, ElementName=mylistbox}" />
This of course defeats the purpose of having the binding on the UserControl itself in the first place. But unless you post some relevant code it's quite hard to tell what is wrong.
Also check the Output window in VisualStudio, binding errors will show up there and might provide valuable information as to what went wrong.

Multiple ListBoxes binding their SelectedItem to the same property in ViewModel - better way?

I have a WPF listview, and in one column the cell may contain one or more ListBoxes.
When I right-click a ListBox I'm building a context menu where each item has a DelegateCommand. Currently I'm setting the command parameter to a SelectedListBox property on the page viewmodel itself as my delegate command needs to know which ListBox has been right-clicked.
However this is leading to weird behaviour, which I'm assuming is because I'm binding multiple ListBoxes to the same page-level property (SelectedListBox).
The relevant XAML for the cell template for the listview is as follows:
<DataTemplate x:Key="MultipleListBoxCellTemplate">
<ListBox SelectedItem="{Binding Path=DataContext.SelectedListBox, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}}" />
</DataTemplate>
Is there a better way to get which ListBox has been right-clicked to my viewmodel, or can anyone think of another approach? Much appreciated :)
When you are building the context menu, you know which list box was selected, yes? I would probably wrap that up in the ICommand you are binding the context item to. This way, each command knows exactly which ListBox it was created by and can get the selected item from there.
Alternately, you may be able to get around the issue with using SelectedItem by changing your binding to OneWayToSource so that the data only flows from the View back to the ViewModel. You may still have timing issues, which I suspect is your current problem, but depending on exactly what is going on, that may resolve it.

WPF: Can I bind to a method of the selected object in another control?

I have two WPF list boxes. One is a list of lists (actually a List of ObservableCollection), the other is a list of all known instances of "Thingy".
Here's the datatemplate I'm using for the "thingy" class.
<DataTemplate DataType="{x:Type Model:Thingy}">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="ThingyInListCheckBox" Click="ThingyInList_Click"></CheckBox>
<TextBlock Text="{Binding Path=ThingyName}"></TextBlock>
</StackPanel>
Here's the XAML for the list boxes:
<ListBox
Name="ListOfGroups"
SelectionMode="Single">
</ListBox>
<ListBox
Name="ListOfThingys"
SelectionMode="Single">
</ListBox>
I have the data binding for the list boxes set up in code, because I'm too tired to figure out how to do it in XAML:
ListOfGroups.ItemsSource = InMemoryCache.ThingyGroups;
ListOfThingys.ItemsSource = InMemoryCache.Thingys;
What I want is the checkbox "ThingyInListCheckBox" to be checked if the 'thingy' object is in the list that is the selected item in the "ListOfGroups" listbox. So basically I need to bind it to the "Contains" method of the "ListOfGroups".SelectedItem while passing it the "ListOfThingys".SelectedItem as a parameter.
I'm tempted to do this all in code, but I'm trying to get a better understanding of XAML data binding because I hate myself and I want me to suffer.
Is this even possible, or have I hit the inevitable "wall of databinding" that exists in every other data binding system in the history of software development?
It is possible, in fact the hard thing is that there are many ways to do this and you have to choose one. None of them is a simple addition to your current code. However there is one way, by which you gain more than solving your problem. Actually, it is more of a pattern, called MVVM (some might argue about the naming).
Here is a small explanation on your example.
Suppose ThingyGroup has an IsSelected property, which is bound to the IsSelected property of the containing ListBoxItem. Again, suppose Thingy has a Group property too. Then you can use Group.IsSelected as a path to bind checkbox. Notice that there is still a small issue that IsSelected is a bool and IsChecked is a nullable bool.
A search on MVVM should give you concrete samples.

Resources