Basic ListView Styling - c#

I have read the docs and tried many samples but to be honest the samples look like a big jumbled mess and don't seem to make much sense.
Can anyone recommend any easy to follow tutorials or docs on how to style a ListView control in XAML? (Without Expression Blend)

The two main common tricks to styling a ListView are to style the items and change the kind of container the listbox uses to lay the items out.
Styling an Item
This basically means setting the ItemTemplate in xaml to something that knows how to dispay the thing that is the content of the listbox's ItemsSource, typically using bindings.
For example, if you have an ObservableCollection<Customer> bound to the listbox where customer is defined as:
public class Order
{
public int Id { get; set; }
public string OrderReference { get; set; }
public string CustomerName { get; set; }
}
Then you might style the items with a data template as follows:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding CustomerName}" />
<TextBlock Text="{Binding OrderReference}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
A basic example but you get the idea.
Changing how items are laid out
Essentially you might not want the default behaviour where items are listed vertically, so you can change the container control used inside the listbox to something more suitable using the ItemsPanel property. If, for example you had an item template that looked like an item from the "large icons" view in Windows Explorer, then you might want the listbox to use a WrapPanel rather than a StackPanel (I'm pretty sure it's a StackPanel):
<ListBox>
<ListBox.ItemsPanel>
<DataTemplate>
<WrapPanel>
<ContentPresenter />
</WrapPanel>
</DataTemplate>
</ListBox.ItemsPanel>
</ListBox>
Again a basic example.
I wrote all this code from memory into StackOverflow so apologies if there are a few typos or mis-remembered bits in there.
HTH.

Check out this 2 part "WPF ListView Styling Tutorial" article: http://blog.vascooliveira.com/wpf-listview-styling-tutorial-part-i/
Note: on my computer, the XML (XAML) code does not display correctly, the < and > signs appear as "&lt" and "&gt".

Related

How to properly remove Items from a ListView when the ItemTemplate is a User Control?

I tried to follow the example here:
WPF ListBox with self-removing items
It made sense but my issue was, the ListView itself is determining the template used. So it can easily customise the bindings to point to the correct target. I am however using MVVM and am struggling to fit the two together.
Example, if the template was:
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyItemView/>
</DataTemplate>
</ListBox.ItemTemplate>
This suddenly becomes more difficult, as ideally, I want to reuse that view without hard coding the bindings.
I tried to use DependencyProperty to pass the List and the Element through, so I could delete it via command.
<ListBox.ItemTemplate Name="myList">
<DataTemplate>
<local:MyItemView TheList={Binding ElementName=myList, Path=DataContext.List} TheElement={Binding}/>
</DataTemplate>
</ListBox.ItemTemplate>
However, I had binding errors telling me that it couldn't convert the value for TheElement from MyClassViewModel to MyClass. Even if I commented that out TheList was always NULL.
Essentially I want:
class MyDataClass { // pretend there's more here}
class MyDataClassContainer
{
public ObservableCollection<MyDataClass> Items;
public void Add(MyDataClass);
public void Remove(MyDataClass);
}
class MyDataClassEntryViewModel
{
public static readonly DependencyProperty ListItemProperty = DependencyProperty.Register("TheClass", typeof(MyDataClass), typeof(MyDataClassEntryViewModel));
public static readonly DependencyProperty ListContainerProperty = DependencyProperty.Register("TheContainer", typeof(MyDataClassContainer), typeof(MyDataClassEntryViewModel));
public MyDataClass TheClass;
public MyDataClassContainer TheContainer;
public ICommand Delete = new DelegateCommand(RemoveItem);
private function RemoveItem(object parameter)
{
TheContainer.Remove(TheClass);
}
}
With the following templates:
MyDataClassEntryView.xaml
<UserControl>
<Grid>
<Button Content="Delete" Command="{Binding Path=Delete}"/>
</Grid>
</UserControl>
MyDataContainerView.xaml
<UserControl>
<ListView x:Name="listView" ItemsSource="{Binding Path=Container.Items}">
<ListView.ItemTemplate>
<DataTemplate>
<local:MyDataClassEntryView TheClass="{Binding}" TheContainer="{Binding ElementName=listView, Path=DataContext.Container}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
Note: I have omitted most of the superfluous lines, as I'm trying to get a generic answer I can use everywhere. Not a hard coded single solution. I was basically want to keep the MVVM structure strong, without lots of hard coded and wiring in the background. I want to use the XAML as much as possible.
All the other methods I see to do with removing from a list, require all sorts of assumptions, such as using the SelectedIndex/Item, or using a method on the ContainerView itself to take the element as a parameter, cast it, then remove, etc. In short, most solutions are far too hard coded to the given examples. It feels like there should be an easy way to achieve this in WPF.
As the ListView issautomatically creating instances of my sub-ViewModel/Views, it's impossible for me to get any data in apparently. I just want to pass parameters along using bindings, basically.
Your button should look like this:
<Button Content="Delete"
Command="{Binding Path=Delete}"
CommandParameter="{Binding}/>
Then the remove command should look something like this:
private function RemoveItem(object parameter)
{
var item = parameter as MyDataClass
if(item != null)
TheContainer.Remove(item);
}
You do not need to pass the list to the UserControl within the ItemTemplate, since it doesn't need to know about the list at all
Edit:
I read over your question a few times to see what you were confused about so I will try to clarify.
Whether the ListView sets its own template in the Xaml, or you use another UserControl, the datacontext still gets passed down to the item. Regardless of how you decide to template the items, the ItemTemplate will have the datacontext of a single item from the ListView's items list.
I think your confusion comes in with having controls outside being brought in for templating. Think of it as if the Xaml from the control you brought in being cut and pasted into the DataTemplate of the ListView when running the program, and then it is really no different from being hard coded in there.
You cannot reach outside of a DataTemplate with Element bindings like you have tried.
Instead you need to use a relativesource like this.
<local:MyItemView TheList="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}, Path=DataContext.List}" />

How to Make Microsoft.VisualStudio.Diagnostics.UI.Controls.MultiSelectComboBox Work

What I Try to Achieve:
I'm developing a Visual Studio plugin, and I need a MultiSelectComboBox. I want to fit in with the look and feel of VisualStudio so it seemed a good idea to use their own class for this:
public class MultiSelectComboBox : UserControl, IComponentConnector, IStyleConnector
Name: Microsoft.VisualStudio.Diagnostics.UI.Controls.MultiSelectComboBox
Assembly: Microsoft.VisualStudio.Diagnostics.Common, Version=12.0.0.0
Microsoft uses this class in their Code Analysis page: View/Other Windows/Code Analysis.
The Problem:
Of course it just doesn't work right when I want to use it. :)
Here's a sample code how I used it:
public TestClass()
{
InitializeComponent();
multiSelectComboBox.ItemsSource = new string[] { "Item 1", "Item 2", "Item 3" };
multiSelectComboBox.AllItemsText = "All items";
}
And here's the XAML markup:
<UserControl ...
xmlns:vsUiControls="clr-namespace:Microsoft.VisualStudio.Diagnostics.UI.Controls;assembly=Microsoft.VisualStudio.Diagnostics.Common"
...>
<vsUiControls:MultiSelectComboBox x:Name="multiSelectComboBox"/>
</UserControl>
Now the MultiSelectComboBox appear and you can interact with it, however, when you select some items, but not all, the items should be displayed like this: Item 1; Item 3 (assuming you selected all but Item 2). However, the displayed text is just Item 1Item 3, totally missing the separator.
The funny thing (that I have overlooked for quite a while) is that if you debug your code and ask for multiSelectComboBox.SelectedItemsText it returns the right values separated with the semicolon.
So the question is, if the value is stored right, why is it not displayed correctly when I use it in my code, but right when used by Microsoft on the Code Analysis page?
The XAML markup describing the style of MultiSelectComboBox contains only one instance of SelectedItemsText, which is a binding. Please see what I've got from .Net Reflector below:
<local:MultiSelectComboBox
p1:UserControl.Name="_this"
p1:AutomationProperties.Name="{Binding RelativeSource={RelativeSource Self},Path=AllItemsText}"
xmlns:p1="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:Microsoft.VisualStudio.Diagnostics.UI.Controls;assembly=Microsoft.VisualStudio.Diagnostics.Common,Version=12.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a">
...
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<local:CheckComboBox
CheckComboBox.Name="_comboBox"
p4:FrameworkElement.Style="{StaticResource ComboStyle}"
p4:Control.HorizontalContentAlignment="Stretch"
p4:KeyboardNavigation.DirectionalNavigation="Continue"
p4:AutomationProperties.Name="{Binding ElementName=_this,Path=SelectedItemsText,Mode=OneWay}"
xmlns:p4="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<ItemsControl.ItemTemplate>
...
</ItemsControl.ItemTemplate>
</local:CheckComboBox>
</Grid>
</local:MultiSelectComboBox>
I am not sure why SelectedItemsText is bound to the AutomationProperties.Name (attached?) property, but this is what .Net Reflector gave me. If I debug my code, I can find the semicolon separated values stored in the Name property of CheckedComboxBox control within the MultiSelectComboBox.
The values seem to be stored right, binding seems to work, yet the text displayed to the UI does not contain the separator. I'm just puzzled...
I took a look at decompiled sources and it seems that property SelectedItemsText is used only for UI automation (attached property AutomationProperties.Name). Real display text is showind with this XAML:
<TextBlock Name="PART_SummaryPartialSelection" Grid.Row="0" Style="{StaticResource DropDownTextBlockStyle}" Visibility="{Binding Path=AllItemsSelected, ElementName=_this, Converter={StaticResource booleanToVisibilityConverterNegative}}">
<ItemsControl Name="PART_Items" Focusable="False" Background="#00FFFFFF" IsHitTestVisible="False" x:Uid="M113" ItemsSource="{Binding SelectedItems, ElementName=_this}" ItemTemplate="{Binding DisplayAreaTemplate, ElementName=_this}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate x:Uid="M115">
<StackPanel IsItemsHost="True" Orientation="Horizontal" x:Uid="M116" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</TextBlock>
So it is just horizontal StackPanel with items one by one without any separator. So you will need to modify this template or just add semicolon at your items (looks like Visual Studio does it this way as it shows semicolon event after last item - item1; item2;)

ContentPresenter in ItemControl.ItemTemplate to show displaymemberpath of itemscontrol

I want to know is there anyway to put contentpresenter in itemtemplate of an itemscontrol to display my data. I don't want hard code binding like Text="{Binding username}" cause I am building a custom control, I think ContentPresenter is what I want. But after I tried using contentpresenter, it give me stackoverflowexception.
<ItemsControl ItemsSource="{Binding SelectedItems, ElementName=listbox}" DisplayMemberPath={Binding DisplayMemberPath}">
<ItemsControl.ItemPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="Separator" Text=", "/>
<ContentPresenter/>
<!--<TextBlock Text="{Binding username}"/>-->
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
That's my code.
If without those seperator and itemtemplate, I able to display my data by just using the displaymemberpath, but it stack all the name together. I still finding any solution to solve it. I hope you can provide some ideas to do this.
The answer is no, you can't. A ContentPresenter is supposed to be used in a ControlTemplate, not a DataTemplate, so it is not the right control to use. From the linked page on MSDN:
You typically use the ContentPresenter in the ControlTemplate of a ContentControl to specify where the content is to be added.
What you can do alternatively, is to declare a number of DataTemplates in a Resources section (complete with Binding Paths) for different types of data and omit the x:Key directives, eg. do not name them. Also, do not specify one for the ItemsControl.ItemTemplate.
When doing this, WPF will implicitly select the correct DataTemplate for the relevant data type and so you can have different outputs for different data types. See the The DataType Property section of the Data Templating Overview page on MSDN for further explanation of this technique.
Yes, and it works well. Outside of a ContentControl's template, you must bind the Content by hand:
<ContentPresenter Content="{Binding username}"/>
I do this a great deal and it never misbehaves. ContentPresenter seems to be implemented for general use. I wonder if the API docs overstate its relationship to ContentControl.
I found an easier way to solve this problem by using horizontal listbox. Thanks for responses

Bind a collection to a WPF ListBox

Update: I've updated the code based on your help so far, and still no luck. When the application loads the ListBox has no items. I assign junk values to Customers in the windows's contructor, and then am also trying to set the ListBox's DataContext as follows:
CustomerList.DataContext = Customers;
--- Original Question (with updated code) ---
I'm having trouble with databinding in a WPF project.
I have a class, Customer, as follows:
public class Customer
{
public String Name { get; set; }
public String Email { get; set; }
}
In my XAML's code behind I have a collection of customers as follows:
public List<Customer> Customers { get; set; }
I'm trying to bind each customer to a ListBox with a ListItemTemplate displaying the customer's information (name/email) in TextBoxes along with a button which locks/unloacks the TextBoxes (sets the IsEnabled property to true or false).
What's the best way to go about this?
So far I've been tryingt he following with no success.
In the XAML I currently have the following (ignoring the toggle part for now, I'm just trying to get the collection itself to be listed.):
<Window.Resources>
<CollectionViewSource x:Key="Customers" Source="{Binding Path=Customers, Mode=TwoWay}"/>
<DataTemplate x:Key="Customer">
<StackPanel Orientation="Horizontal">
<TextBox Content="{Binding Name}" />
<TextBox Content="{Binding Email}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox ItemsSource="{Binding Source={StaticResource Customers}}"
ItemTemplate="{StaticResource ResourceKey=Customer}"
Name="CustomerList"
Height="300" />
</StackPanel>
You need to change
ItemsSource="{Binding Source=Customers}"
to
ItemsSource="{Binding Source={StaticResource Customers}}" DataContext="{StaticResource Customers}"
Code similar to the updated one works for me after changing
<TextBox Content="{Binding Name}" />
to
<TextBox Text="{Binding Name}" />
As TextBox doesn't have Content property(like a Label), the former refused to compile in VS.
Well, it is set to Text in definition:
[ContentPropertyAttribute("Text")]
public class TextBox : TextBoxBase, IAddChild
But I thought it is only used between the brackets(<TextBox>Like so</TextBox>)?
Could this be the source of the problem?
Try setting the ItemsSource of your CustomerList as follows: ItemsSource="{Binding}". You've set the DataContext of the ListBox to the list of customers, you need to set the ItemsSource to the same collection, hence, the direct binding.
Another thing that you can do, in case you prefer to use the CollectionViewSource, is to set the DataContext of your window to the same class DataContext=this, because without this, the resource definition won't be able to locate the "Customers" collection that you defined in the code behind. If you do this, however, you don't need CustomerList.DataContext = Customers; because you're directly assigning the ItemsSource to a static resource, not relatively to the DataContext.
One more thing. I think you should give the CollectionViewSource and the corresponding collection in the code behind different names. This isn't going to cause a runtime issue, but it makes it hard to maintain the code ;)
Hope this helps :)

What kind of control do I need for a vertical display of a List<CustomObject> with a CustomTemplate?

Let's say I have a List<MyObject>,
which stores
class MyObject
{
public String Headline {get;set;}
public DateTime MyDate {get;set;}
}
I want to display each MyObject in a vertical order, so that it looks like a entry in a List, with rounded corners and the values are written over the background image.
I've googled two pictures of what looks close to what kind of display control I want to have:
http://assets.gearlive.com/blogimages/gallery/sonos-iphone-app/sonos-iphone-zone-menu_medium.jpg
http://iconfactory.com/twitterrific_touch/images/screenshot_list.png
What kind of control can I use for this? It's like a Repeater/ListView with a custom template. (Let's assume I don't have any graphic skills. How can I create a rectangle with rounded cornes and a color gradient as a background?)
(I'm new to WPF)
If you don't want selection, go for an ItemsControl. Otherwise use ListBox.
ListBox inherits from Selector which inherits from ItemsControl and adds selection functionality.
XAML:
<ItemsControl Name="ItemsControl1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="<Binding Headline}" />
<TextBlock Text="<Binding MyDate}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Code behind:
ItemsControl1.ItemsSource = ListOfMyObjects;
you should probably use a ListBox. then, make a DataTemplate for your object type--here is a nice explanation on DataTemplates. You should also make MyObject derive from INotifyPropertyChanged so that when you change a value in the object, it will automatically update your UI. One other thing, you should probably change from List<MyObject> to ObservableCollection<MyObject> so that the ListBox will automaticaly update when you add/remove items

Categories

Resources