I want to change the style of each item in the listbox like this
But in my case, I want only to change the color and font style (italic, bold) of each item, the items in the listbox is added programmatically
how could I change the style of each item?
You can create a view model class for the data items that has all necessary properties, like
class DataItem
{
public string Text { get; set; }
public Brush Foreground { get; set; }
public FontStyle FontStyle { get; set; }
public FontWeight FontWeight { get; set; }
}
and bind to these properties in the ItemTemplate of the ListBox. The example below assumes that Items is a property in your view model that returns a collection of DataItem objects.
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"
Foreground="{Binding Foreground}"
FontStyle="{Binding FontStyle}"
FontWeight="{Binding FontWeight}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you prefer to use boolean properties like e.g. IsBold in your item view model, you may add an ItemContainerStyle with DataTriggers that set the appropriate ListBoxItem properties:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"
Foreground="{Binding Foreground}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding IsItalic}" Value="True">
<Setter Property="FontStyle" Value="Italic"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsBold}" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Related
I'm developing an application in C# MVVM
My question is about adding a tooltip for each item that the ComboBox is binded to. Since there are two items only I want it to show the tooltip whenever I open the dropdown and hover my mouse over one of the items , like this:
if I hover my mouse over the first element in the dropDown I'll get a ToolTip with " first item".. and "second item" when I hover over the second element.
ComboBox is placed in DataGridTemplateColumn -> Cell Template -> DataTemplate
<DataGridTemplateColumn Header="PRĄD POJEMNOŚCIOWY [A]" HeaderStyle="{StaticResource PRAD_POJEMNOSCIOWY}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="PradPojemnosciowyComboBox"
SelectedValue="{Binding SelectedItem, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=LiniaWyComboBox, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEditable="True"
IsReadOnly="False"
Text="{Binding Prad_pojemnosciowy, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsTextSearchEnabled="False"
IsSynchronizedWithCurrentItem="True"
PreviewKeyDown="PradPojemnosciowyComboBox_OnPreviewKeyDown">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<Trigger Property="SelectedValue" Value="{x:Null}">
<Setter Property="SelectedIndex" Value="{Binding LiniaWyComboBox}"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
UPDATE
ToolTipLabel.cs:
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace GPZmodel.UserControlsGraphicGenerators
{
public class ToolTipLabel : INotifyPropertyChanged
{
private string _toolTipText;
public string ToolTipText
{
get { return _toolTipText;}
set
{
if (_toolTipText != value)
{
_toolTipText = value;
}
}
}
public ObservableCollection<ToolTipLabel> ToolTipList = new ObservableCollection<ToolTipLabel>()
{
new ToolTipLabel() {ToolTipText = "Nazwa1"} ,
new ToolTipLabel() {ToolTipText = "Nazwa2"} ,
};
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
You could use an ItemContainerStyle:
<ComboBox Name="PradPojemnosciowyComboBox"
SelectedValue="{Binding SelectedItem, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=LiniaWyComboBox, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEditable="True"
IsReadOnly="False"
Text="{Binding Prad_pojemnosciowy, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsTextSearchEnabled="False"
IsSynchronizedWithCurrentItem="True"
PreviewKeyDown="PradPojemnosciowyComboBox_OnPreviewKeyDown">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<Trigger Property="SelectedValue" Value="{x:Null}">
<Setter Property="SelectedIndex" Value="{Binding LiniaWyComboBox}"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="ToolTip">
<Setter.Value>
<TextBlock Text="{Binding}" />
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Bind the TextBlock in the ToolTip to whatever property of your data object that you want to display.
The tooltip for the below listbox is set using a setter. Nothing appears for a tooltip on mouse over.
I suspect the issue is the itemssource of the listbox itself. The listbox is bound to a list of AttributeItems called CandidateAttributes. An element of that list is an observablecollection called AttributePath, and the property in the Attribute path I am trying to bind the tooltip to is called ConceptualPath. Below is the definition for CandidateAttributes-
public static List<AttributeItem> CoBRRaAttributes { get; set; }
The AttributeItems class-
public class AttributeItem
{
private string _displayName = "";
private ObservableCollection<CoBRRa_WPF.CoBRRaUtilities.ViewModels.QueryTool.AttributeCollection> _AttributePath;
public AttributeItem(int id, string displayName, ObservableCollection<CoBRRa_WPF.CoBRRaUtilities.ViewModels.QueryTool.AttributeCollection> attributePath)
{
DisplayName = displayName;
AttributePath = attributePath;
}
public ObservableCollection<CoBRRa_WPF.CoBRRaUtilities.ViewModels.QueryTool.AttributeCollection> AttributePath
{
get
{
return _AttributePath;
}
set
{
_AttributePath = value;
}
}
}
The xmal-
<ListBox
Name="lstCandidates"
ItemsSource="{Binding Path=UIProperties.CandidateAttributes}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}">
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Control.ToolTip" Value="{Binding AttributePath.ConceptualPath}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I can substitute some text in the place of Binding AttributePath.ConceptualPath and the tooltip displays that text. Just can't figure out why it does not work in the binding. How can I get this to work?
You are binding to AttributePath.ConceptualPath but AttributePath returns an ObservableCollection<AttributeCollection> and this one has no ConceptualPath property.
You should either change the type of the AttributePath property to just CoBRRa_WPF.CoBRRaUtilities.ViewModels.QueryTool.AttributeCollection or bind to a specific AttributeCollection, for example the first one:
<Setter Property="Control.ToolTip" Value="{Binding AttributePath[0].ConceptualPath}"/>
Also make sure that ConceptualPath is a public property of the AttributeCollection class.
Edit:
If you want to the display a list of paths in the tooltip, you should use an ItemsControl:
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Control.ToolTip">
<Setter.Value>
<ItemsControl ItemsSource="{Binding AttributePath}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ConceptualPath}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Setter.Value>
</Setter>
</Style>
How can i stretch the header width in a grouped listbox?
The listbox ItemSource is bound to hierarchical data structure. A group item contains a name and a list of subitems.
public class MyGroup
{
public string GroupName { get; set; }
public ObservableCollection<MyItem> SubItems { get; }
}
public class MyItem
{
public string ItemName { get; set; }
public DateTime Creation { get: set: }
}
public class MyViewModel:
{
public ObservableCollection<MyGroup> Data { get; set; }
}
The page is defined as follows
<Page>
<Page.Resources>
<CollectionViewSource x:Name="cvsListBoxGroup" IsSourceGrouped="True" Source="{Binding Data}" ItemsPath="SubItems"/>
</Page.Resources>
<Page.DataContext>
<local:MyViewModel/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListBox Width="500" SelectionMode="Single" ItemsSource="{Binding Source={StaticResource cvsListBoxGroup}}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="Beige">
<TextBlock Text="{Binding GroupName}"></TextBlock>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Bisque">
<TextBlock Text="{Binding ItemName}" Foreground="Black" ></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Page>
Groups and items are displayed. The items don't fill the listbox full width so we need to set the HorizontalContentAlignment for ListBox.ItemContainerStyle. This trick is common - no problem
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
I tried to figure out some similiar trick for the groupheader items - but nothing helps.
<GroupStyle.HeaderContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</GroupStyle.HeaderContainerStyle>
The problem seems the TargetType. For a listview there is a ListViewHeaderItem. But nothing similar exists for the listbox.
See the answer here, it's for ListView but that's basically the same:
https://social.msdn.microsoft.com/Forums/windowsapps/en-US/e8d4f7b3-93a8-4f17-9679-8b700b8d02e6/how-to-adjust-the-width-of-group-header-in-listview-to-stretch-full-width?forum=winappswithcsharp
So I have a ComboBox in a UserControl. This UserControl is shown when an item is selected from a ListView and is responsible for editing the values of the selected object. The problem is that if I have in the ListView more objects with the same value that the ComboBox is responsible to change, all of this values are change to the value of the selected item in the ListView.
I have this ListView:
<ListView x:Name="AcorduriListView" ItemsSource="{Binding ., UpdateSourceTrigger=PropertyChanged}"
ItemContainerStyle="{StaticResource AcordListViewItem}" />
With this template:
<Style TargetType="ListViewItem" x:Key="AcordListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid
d:DataContext="{d:DesignData Source=DesignTimeData/AcordSampleData.xaml, IsDesignTimeCreatable=True}">
...
<TextBlock x:Name="CategorieDescriptionTextBlock" Padding="3,0,0,0" Foreground="Black"
Visibility="{Binding ElementName=CategorieTextBlock, Path=Text, Converter={StaticResource StringNullOrEmptyToVisibilityConverter}}"
Text="Completează categoria" />
<TextBlock x:Name="CategorieTextBlock"
Padding="3,0,0,0" Foreground="Black"
Text="{Binding Categorie.Nume}"
Visibility="{Binding ElementName=CategorieDescriptionTextBlock, Path=IsVisible, Converter={StaticResource InverteBooleanToVisibilityConverter}}" />
...
</StackPanel>
</Border>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="rootPanel" Property="Background"
Value="{StaticResource AccentColorBrush}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="rootPanel" Property="Background"
Value="{StaticResource AccentColorBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The ListView is bound to a collection of this Object:
public class Acord : INotifyPropertyChanged, IChangeTracking
{
public string Name{ get; set; }
...
public int CategorieId { get; set; }
public virtual Categorie Categorie { get; set; }
}
where Categorie is:
public class Categorie : INotifyPropertyChanged
{
public string Nume { get; set; }
....
private ObservableCollection<Acord> _acorduri;
public virtual ObservableCollection<Acord> Acorduri{ get; set; }
}
When an item from this ListView is Selected, a UserControl is shown based on a trigger on the ListView SelectedIndex. The UserControl has it's DataContext bind to the ListView SelectedItem (an Acord object). From the many controls in this UserControl I have a Combobox CategorieAcordComboBox that has it's ItemsSource bind to a collection of Categorie objects.
<ComboBox Grid.Row="2" Margin="5,0,5,5" x:Name="CategorieAcordComboBox"
DisplayMemberPath="Nume" SelectedValuePath="CategorieId"
Text="{Binding Categorie.Nume}"
IsSynchronizedWithCurrentItem="False" />
<DatePicker Grid.Row="3" Margin="5" FontSize="16" x:Name="DataAcorDatePicker"
controls:TextBoxHelper.Watermark="Data"
SelectedDate="{Binding Data, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Grid.Row="4" x:Name="LocatieAcordTextBox" Margin="5"
controls:TextBoxHelper.UseFloatingWatermark="True"
controls:TextBoxHelper.Watermark="Locație" FontSize="16"
Text="{Binding Locatie, UpdateSourceTrigger=PropertyChanged}" />
My problem is that when I select a diffrent item in ComboBox, while the Text binding is set to Categorie.Nume the content of all the TextBoxes CategorieTextBlock from the template that have the same value, changes to the selected value from ComboBox.
Video: http://screencast.com/t/bbRsb6RMwkZ7
I have a popup, where I want to display different things depending on various buttons which are clicked. To do this I've added a ContentPresenter nad in this ContentPresenter I've got an TemplateSelector. My problem is that as far as I can see it only checks which template to use the first time my popUp is run and uses this template from then on. Is there a way to get the code to change the template to use?
The code I've got so far is (xaml):
<Popup IsOpen="{Binding IsOpen}" Height="{Binding Height}" Width="{Binding Width}">
<Grid>
<ContentPresenter x:Name="CP" Loaded="CP_Loaded">
<ViewModel:PopUpTemplateSelector x:Name="PUT" Content="{Binding}">
<ViewModel:PopUpTemplateSelector.View1>
<DataTemplate>
<View:View1/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View1>
<ViewModel:PopUpTemplateSelector.View2>
<DataTemplate>
<View:View2/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View2>
<ViewModel:PopUpTemplateSelector.View3>
<DataTemplate>
<View:View3/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View3>
<ViewModel:PopUpTemplateSelector.View4>
<DataTemplate>
<View:View4/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View4>
<ViewModel:PopUpTemplateSelector.View5>
<DataTemplate>
<Design:View5/>
</DataTemplate>
</ViewModel:PopUpTemplateSelector.View5>
</ViewModel:PopUpTemplateSelector>
</ContentPresenter>
</Grid>
</Popup>
and my popUpTemplateSelector(C#) is
public class PopUpTemplateSelector : DataTemplateSelector
{
public DataTemplate View1{ get; set; }
public DataTemplate View2 { get; set; }
public DataTemplate View3 { get; set; }
public DataTemplate View4 { get; set; }
public DataTemplate View5 { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
PopUpViewModel Pop = item as PopUpViewModel;
if(Pop.TemplateToUse == "View1")
{
return View1;
}
else if(Pop.TemplateToUse == "View2")
{
return View2;
}
else if(Pop.TemplateToUse.Equals("View3"))
{
return View3;
}
else if (Pop.TemplateToUse.Equals("View4"))
{
return View4;
}
else if(Pop.TemplateToUse.Equals("View5"))
{
return View5;
}
return null;
}
}
I would suggest you use DataTriggers bound to TemplateToUse property on the ViewModel to update ContentTemplate. And also use ContentControl instead of ContentPresenter
<Popup IsOpen="{Binding IsOpen}" Height="{Binding Height}" Width="{Binding Width}">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="View1Template">
<View:View1/>
</DataTemplate>
<DataTemplate x:Key="View2Template">
<View:View2/>
</DataTemplate>
<DataTemplate x:Key="View3Template">
<View:View3/>
</DataTemplate>
<DataTemplate x:Key="View4Template">
<View:View4/>
</DataTemplate>
<DataTemplate x:Key="View5Template">
<Design:View5/>
</DataTemplate>
<Style x:Key="ContentStyle" TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View1">
<Setter Property="ContentTemplate" Value="{StaticResource View1Template}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View2">
<Setter Property="ContentTemplate" Value="{StaticResource View2Template}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View3">
<Setter Property="ContentTemplate" Value="{StaticResource View3Template}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View4">
<Setter Property="ContentTemplate" Value="{StaticResource View4Template}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=TemplateToUse}" Value="View5">
<Setter Property="ContentTemplate" Value="{StaticResource View5Template}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ContentControl x:Name="CP" Loaded="CP_Loaded" Style="{StaticResource ContentStyle}" Content="{Binding}" />
</Grid>
</Popup>