Suppose my Model class called person looks like below code:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public Gender Gender { get; set; }
}
Gender class used in Person class looks like:
public class Gender
{
public int ID { get; set; }
public string Name { get; set; }
public string ImageData { get; set; }
}
Now in a view called EditView I am trying to display the information of currently selected Person:
<Page ...............>
<Page.DataContext>
<vm:EditViewModel />
</Page.DataContext>
<Grid DataContext="{Binding CurrentPerson}">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<TextBox Text="{Binding Name}" />
<ComboBox Grid.Row="1"
ItemsSource="{Binding DataContext.Genders, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"
SelectedItem="{Binding Gender}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Padding="6"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="gd" TextElement.Foreground="Black">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Path Grid.Column="0" Data="{Binding ImageData}" Stretch="Uniform" Fill="Black" Width="24" Height="24" Margin="4" RenderTransformOrigin="0.5,0.5" />
<TextBlock Grid.Column="1" Text="{Binding Name}" Padding="6"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ComboBoxItem.IsSelected" Value="True">
<Setter TargetName="gd" Property="Background" Value="#FF4CC4F6"></Setter>
<Setter TargetName="gd" Property="TextElement.Foreground" Value="White"></Setter>
</Trigger>
<Trigger Property="ComboBoxItem.IsMouseOver" Value="True">
<Setter TargetName="gd" Property="Background" Value="#FF84CDFA"></Setter>
<Setter TargetName="gd" Property="TextElement.Foreground" Value="White"></Setter>
</Trigger>
<Trigger Property="ComboBoxItem.IsHighlighted" Value="True">
<Setter TargetName="gd" Property="Background" Value="LightSkyBlue"></Setter>
<Setter TargetName="gd" Property="TextElement.Foreground" Value="Black"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
</Page>
EditViewModel.cs code:
public class EditViewModel : ViewModelBase
{
public EditViewModel()
{
Genders = new ObservableCollection<Gender>(
from gender in XDocument.Load(DirectoryPaths.DataDirectory + #"Basic\Genders.xml")
.Element("Genders").Elements("Gender")
select new Gender
{
Id = Convert.ToInt32(gender.Attribute("Id").Value),
Name = gender.Element("Name").Value,
ImageData = gender.Element("ImageData").Value
}
);
}
private ObservableCollection<Gender> _genders;
public ObservableCollection<Gender> Genders
{
get
{
return _genders;
}
set
{
_genders = value;
NotifyPropertyChanged("Genders");
}
}
private Person _currentPerson;
public Person CurrentPerson
{
get
{
return _currentPerson;
}
set
{
_currentPerson = value;
NotifyPropertyChanged("CurrentPerson");
}
}
}
I have added the just relevent code for viewmodel. The property CurrentPerson has a person when EditView is shown. But in the ComboBox I do not get any selection by default. I am able to select values manually. But when the EditView loads I am not able to say the gender of the person as it does not get displayed as SelectedItem of the ComboBox.
You are running into a very common problem, two identical Gender objects are not equal to each other!
ComboBox sees the bound selected item, but doesn't see a matching item (actually, it doesn't see a matching reference) so it doesn't select anything.
To fix it, Gender needs to override Object.Equals (MSDN) and Object.GetHashCode (MSDN). According to this article implementing IEquatable will also work.
Related
I have multiple objects with multiple properties which I want to display or hide (or create if needed) based on the property value.
For example:
Model:
public class InputModel
{
public string Name { get; set; }
public string Id { get; set; }
public string Type { get; set; }
public bool Required { get; set; }
public int Dpi { get; set; }
public string Data { get; set; }
public List<ColorModel> Filter { get; set; }
public List<RenderModel> Render { get; set; }
public List<LayoutModel> Layout { get; set; }
}
View:
<ItemsControl x:Name="InputsList"
Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Height="400">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Center" Background="Beige" Margin="10">
<TextBlock Text="{Binding Name, StringFormat='Name: {0}'}"/>
<TextBlock Text="{Binding Id, StringFormat='Id: {0}'}"/>
<TextBlock Text="{Binding Type, StringFormat='Type: {0}'}"/>
<CheckBox Grid.Column="1" Content="Required" Margin="0,5,0,0"
IsChecked="{Binding ElementName=Required, Path=CheckBoxIsChecked}"/>
<TextBox Grid.Column="0" Grid.Row="4"
Text="{Binding Dpi, StringFormat='Dpi: {0}'}"/>
<TextBlock Grid.Column="0" Grid.Row="5"
Text="Render:"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And my ViewModel:
public class InputsListViewModel : Screen
{
private IRootModel _rootModel;
public InputsListViewModel(IRootModel rootModel)
{
_rootModel = rootModel;
}
private BindingList<InputModel> _inputs;
public BindingList<InputModel> InputsList
{
get
{
_inputs = new (_rootModel.Inputs);
return _inputs;
}
set
{
_inputs = value;
NotifyOfPropertyChange(() => InputsList);
}
}
private BindingList<RenderModel> _renders;
public BindingList<RenderModel> RenderList
{
get
{
_renders = new(_rootModel.Render);
return _renders;
}
set
{
_renders = value;
NotifyOfPropertyChange(() => RenderList);
}
}
private BindingList<LayoutModel> _layouts;
public BindingList<LayoutModel> LayoutList
{
get
{
_layouts = new (_rootModel.Layout);
return _layouts;
}
set
{
_layouts = value;
NotifyOfPropertyChange(() => LayoutList);
}
}
}
I don't need to display all of the properties in here, just the ones that I need at a given time. And I want to be able to add any of the properties that are missing, if I need to.
I am a beginner and so far I only know how to manually create the fields that I need in view and bind to them.
Thanks!
you can hide whole items with ItemContainerStyle or individual Properties as shown in 2 different ways
<ItemsControl ItemsSource="{Binding MyItems}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Visibility" Value="{Binding ShowInUi, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name, StringFormat='Name: {0}'}"/>
<TextBlock Text="{Binding Id, StringFormat='Id: {0}'}"/>
<TextBlock Text="{Binding Type, StringFormat='Type: {0}'}"/>
<!--possibility 1-->
<TextBlock x:Name="AdditionalInfoText" Text="{Binding AdditionalInfo}"/>
<!--possibility 2-->
<TextBlock Text="{Binding SuperDuperValue}" Visibility="{Binding IsSuperDuperItem, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding AdditionalInfo}" Value="{x:Null}">
<Setter TargetName="AdditionalInfoText" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
For all textblocks in an area
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
<DataTrigger Binding="{Binding Text.Length, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
I am trying to change the CurrentCarViewModel's model Type based on the selected value in a ListBox. Currently, in the ListBox's SelectionChanged event I use a switch statement to create a new model with the correct type inside CurrentCarViewModel. I don't think it's the best solution, because I feel switching the type every time I want to operate inside the CarViewModel is not good.
This is my MainWindow.xaml:
public partial class MainWindow : Window
{
public CarViewModel CurrentCarViewModel { get; set; } = new CarViewModel();
public MainWindow()
{
InitializeComponent();
...
}
private void CarType_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch ((sender as ListBox).SelectedIndex)
{
...
}
}
}
public enum CarType { ElectricCar, GasCar }
public class CarViewModel
{
public Car Model { get; set; }
public CarType CarType { get; set; }
}
public class Car { }
public class ElectricCar : Car { }
public class GasCar : Car { }
and here's the ListBox:
<ListBox SelectionChanged="CarType_SelectionChanged" Grid.Row="0">
<ListBoxItem Content="Electric Car"/>
<ListBoxItem Content="Gas Car"/>
</ListBox>
This operation is not simple, so I made it as a sample.
I made a sample of the source code you want. Please check it.
GitHub
Xaml
<ListBox ItemsSource="{Binding Cars}"
SelectedItem="{Binding CurrentCar}"/>
ViewModel
{
public class MainViewModel
{
private Car _currentCar;
private Car CurrentCar
{
get { return _currentCar; }
set { _currentCar = value; CarChanged(value); }
}
public List<Car> Cars { get; set; }
public MainViewModel()
{
Cars = new List<Car>
{
new Electric { Name = "ModelS", Brand = "TESLA", BatteryCharge = 100 },
new Gasoline { Name = "E-Class", Brand = "BENZ", Displacement = 2000 },
new Gasoline { Name = "5-Series", Brand = "BMW", Displacement = 2000 }
};
}
private void CarChanged(Car value)
{
if (value is Electric ele)
{
// Electric
Debug.WriteLine(ele.GetType().ToString());
}
else if (value is Gasoline gas)
{
// Gasolin
Debug.WriteLine(gas.GetType().ToString());
}
}
}
ListBoxItem Resource
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Foreground" Value="#FFFFFF"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border Background="{TemplateBinding Background}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="COL_A"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="COL_B"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Viewbox Grid.Column="0" Width="16" Height="16" Margin="4 4 8 4" VerticalAlignment="Center">
<Path x:Name="path" Width="24" Height="24"/>
</Viewbox>
<TextBlock Grid.Column="1" Text="{Binding Name}" Margin="0 0 6 0" VerticalAlignment="Center" FontWeight="Bold"/>
<TextBlock Grid.Column="2" Text="{Binding Brand}" Margin="0 0 6 0" VerticalAlignment="Center"/>
<TextBlock Grid.Column="3" Text="{Binding Type}" Margin="0 0 0 0" VerticalAlignment="Center" Foreground="#cccccc"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FF37528B"/>
</Trigger>
<DataTrigger Binding="{Binding Type}" Value="{x:Type model:Electric}">
<Setter TargetName="path" Property="Data" Value="{StaticResource GEO.BATTERY}"/>
<Setter TargetName="path" Property="Fill" Value="#FDFD86"/>
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="{x:Type model:Gasoline}">
<Setter TargetName="path" Property="Data" Value="{StaticResource GEO.GASOLINE}"/>
<Setter TargetName="path" Property="Fill" Value="#FFACBBCD"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Grid.IsSharedSizeScope" Value="True"/>
</Style>
Path
<Geometry x:Key="GEO.GASOLINE">M3,2H6C6.28,2 6.53,2.11 6.71,2.29L8.79,4.38L9.59,3.59C10,3.2 10.5,3 11,3H17C17.5,3 18,3.2 18.41,3.59L19.41,4.59C19.8,5 20,5.5 20,6V19A2,2 0 0,1 18,21H8A2,2 0 0,1 6,19V13L6,12V8C6,7.5 6.2,7 6.59,6.59L7.38,5.79L5.59,4H3V2M11,5V7H17V5H11M11.41,11L9.41,9H8V10.41L10,12.41V15.59L8,17.59V19H9.41L11.41,17H14.59L16.59,19H18V17.59L16,15.59V12.41L18,10.41V9H16.59L14.59,11H11.41M12,13H14V15H12V13Z</Geometry>
<Geometry x:Key="GEO.BATTERY">M16,15H8V6H16M16.67,4H15V2H9V4H7.33A1.33,1.33 0 0,0 6,5.33V20.67C6,21.4 6.6,22 7.33,22H16.67A1.33,1.33 0 0,0 18,20.67V5.33C18,4.6 17.4,4 16.67,4Z</Geometry>
I have a sidebar (in a C# WPF program) that should display 4 "different" buttons (They are actually 2 different styles, which both have another style for the active state). The sidebar consists of an ItemsControl. I've now managed to create a list where the correct style is used based on an enum value (as shown below). Here's a small question: Can I do it this way, or should I rewrite it, and if so, how could something like this be built? Keywords or something that I have to look at are enough for me.
My real question now is: I have bound a command to every button, nothing complicated at first. The command now sets its own state to NormalActive for testing purposes. The 1st item in this list should be set from LiveActive to Live (so that you always see the currently selected item as you know it). And here's the problem: The button can set its own state, so when I click on button 3, the state of button 3 is set from Normal to NormalActive. But what doesn't happen is the change from LiveActive to Active from the 1st button. Even if I output the current state to the console before and after the change, it returns LiveActive for both. I also tried invoking the whole thing into the dispatcher if I'm not in the UI thread for some reason, it didn't work. So the button can set its own state, but not the one of another. But I don't get an error message or anything. Also the setter method of the property is called, it just doesn't change it. What could be the reason?
PluginListControl:
<Grid DataContext="{x:Static local:PluginListDesignModel.Instance}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:PluginListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
PluginListItemControl:
<UserControl.Resources>
<DataTemplate x:Key="PluginTile" DataType="{x:Type local:PluginListItemViewModel}">
<Button Style="{StaticResource PluginTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate x:Key="PluginActiveTile" DataType="{x:Type local:PluginListItemViewModel}">
<Button Style="{StaticResource PluginActiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate x:Key="PluginLiveTile" DataType="{x:Type local:PluginListItemViewModel}">
<Button Style="{StaticResource PluginLiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate x:Key="PluginActiveLiveTile" DataType="{x:Type local:PluginListItemViewModel}">
<Button Style="{StaticResource PluginActiveLiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</UserControl.Resources>
<ContentControl d:DataContext="{x:Static local:PluginListItemDesignModel.Instance}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="0">
<Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource PluginActiveTile}" />
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="2">
<Setter Property="ContentTemplate" Value="{StaticResource PluginLiveTile}" />
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="3">
<Setter Property="ContentTemplate" Value="{StaticResource PluginActiveLiveTile}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
PluginListItemViewModel: (The ViewModel for each list item)
public class PluginListItemViewModel : BaseViewModel
{
public string Name { get; set; }
public PluginTileStates State { get; set; }
public ICommand SetStateCommand { get; set; }
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public PluginListItemViewModel()
{
SetStateCommand = new RelayCommand(() => SetState());
}
#endregion
private void SetState()
{
PluginListDesignModel.Instance.Items[0].State = PluginTileStates.Live;
State = PluginTileStates.NormalActive;
}
}
Steps to reproduce:
Create a new WPF project, .NET Framework 4.6.1 (Visual Studio 2017).
Replace the grid in MainWindow with the following:
<Grid DataContext="{x:Static local:ListViewModel.Instance}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
Add a new UserControl named ListItemControl and replace the grid with:
<UserControl.Resources>
<Style x:Key="Tile" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
</Style>
<Style x:Key="ActiveTile" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Green" />
</Style>
<DataTemplate x:Key="PluginTile" DataType="{x:Type local:ListItemViewModel}">
<Button Width="100" Height="60" Style="{StaticResource Tile}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
<DataTemplate x:Key="PluginActiveTile" DataType="{x:Type local:ListItemViewModel}">
<Button Width="100" Height="60" Style="{StaticResource ActiveTile}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</DataTemplate>
</UserControl.Resources>
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="0">
<Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="1">
<Setter Property="ContentTemplate" Value="{StaticResource PluginActiveTile}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Add a new class called BaseViewModel and replace class with:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
public void OnPropertyChanged(string name)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
Add new class called ListItemViewModel and replace class with:
public enum TileStates
{
Normal = 0,
Active = 1
}
public class ListItemViewModel : BaseViewModel
{
public TileStates State { get; set; }
public ICommand SetStateCommand { get; set; }
public ListItemViewModel()
{
SetStateCommand = new RelayCommand(() =>
{
ListViewModel.Instance.Items[0].State = TileStates.Normal;
State = TileStates.Active;
});
}
}
Add new class called ListViewModel and replace class with:
public class ListViewModel : BaseViewModel
{
public static ListViewModel Instance => new ListViewModel();
public List<ListItemViewModel> Items { get; set; } = new List<ListItemViewModel>
{
new ListItemViewModel
{
State = TileStates.Active
},
new ListItemViewModel
{
State = TileStates.Normal
}
};
}
Add new class called RelayCommand and replace class with:
public class RelayCommand : ICommand
{
private Action mAction;
public event EventHandler CanExecuteChanged = (sender, e) => { };
public RelayCommand(Action action)
{
mAction = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
mAction();
}
}
Install the NuGet-Packages: "Fody v4.0.2" and "PropertyChanged.Fody v2.6.0" (You probably have to restart Visual Studio after installation
If you now press the bottom button, it should get green and the top one should switch to red.
ListViewModel.Instance returns a new instance of the ListViewModel class each time it's invoked. It should return the same instance:
public static ListViewModel Instance { get; } = new ListViewModel();
I have a grid view with a CollectionViewSource as it's items source.
I want to to bind the background property of each group container panel so that each group has its own background color.
how can this be achieved?
I'm trying to use binding in the <GroupStyle.ContainerStyle> of the gridview but can't get it to work.
Since the list will be grouped already, then applying a background on each GridViewItem will do the trick, depending on whether you want to define the backgound in each item as a property or use a converter to do that :
public class Data
{
public String Prop1 { get; set; }
public String Prop2 { get; set; }
public SolidColorBrush GroupeBrush { get; set; } //the groupe background color
}
And the xaml,
<Page.Resources>
<CollectionViewSource x:Name="DataCollection" IsSourceGrouped="true" />
</Page.Resources>
<Grid>
<GridView SelectionMode="None" ItemsSource="{Binding Source={StaticResource DataCollection}}" >
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
<Setter Property="VerticalContentAlignment" Value="Stretch"></Setter>
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="{Binding GroupeBrush}">
<TextBlock Text="{Binding Prop2}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
Or you could as well play around the GridView GroupStyle although you will need to find a way to bind the background from the Style Setter :
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border Background="Black" HorizontalAlignment="Stretch">
<TextBlock Text='{Binding Key}' Foreground="White" Margin="5" Style="{StaticResource SubheaderTextBlockStyle}" />
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="MinWidth" Value="600"/>
<Setter Property="BorderBrush" Value="DarkGray"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Margin" Value="3,0"/>
<Setter Property="Background" Value="BurlyWood"/>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</GridView.GroupStyle>
Here the entire code behind in case any one wants to experiment more
public sealed partial class MainPage : Page
{
private ObservableCollection<Data> _datas = new ObservableCollection<Data>()
{
new Data()
{
Prop1 = "val1",
Prop2 = "val2",
GroupeBrush=new SolidColorBrush(Colors.Blue)
}, new Data()
{
Prop1 = "val1",
Prop2 = "val2",
GroupeBrush=new SolidColorBrush(Colors.Blue)
}, new Data()
{
Prop1 = "val1",
Prop2 = "val3",
GroupeBrush=new SolidColorBrush(Colors.Blue)
}, new Data()
{
Prop1 = "val2",
Prop2 = "val4",
GroupeBrush=new SolidColorBrush(Colors.Green)
}, new Data()
{
Prop1 = "val3",
Prop2 = "val5",
GroupeBrush=new SolidColorBrush(Colors.Red)
},
};
public ObservableCollection<Data> Datas
{
get
{
return _datas;
}
set
{
if (_datas == value)
{
return;
}
_datas = value;
}
}
public MainPage()
{
this.DataContext = this;
InitializeComponent();
DataCollection.Source = GetAllGrouped();
}
public IEnumerable<IGrouping<string, Data>> GetAllGrouped()
{
return Datas.GroupBy(x => x.Prop1);
}
}
public class Data
{
public String Prop1 { get; set; }
public String Prop2 { get; set; }
public SolidColorBrush GroupeBrush { get; set; } //the groupe background color
}
OK, here's what I've got. I had to modify the template of the Group Container Style:
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Margin" Value="10,0,0,0" />
<Setter Property="Padding" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Border Background="{Binding Group, Converter={StaticResource ThemeColorConverter}}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl x:Name="HeaderContent"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
Margin="{TemplateBinding Padding}"
TabIndex="0"
IsTabStop="False" />
<ItemsControl x:Name="ItemsControl"
Grid.Row="1"
ItemsSource="{Binding GroupItems}"
IsTabStop="False"
TabNavigation="Once"
TabIndex="1" >
<ItemsControl.ItemContainerTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
<ContentThemeTransition />
<ReorderThemeTransition />
<EntranceThemeTransition IsStaggeringEnabled="False" />
</TransitionCollection>
</ItemsControl.ItemContainerTransitions>
</ItemsControl>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
This line did the trick:
<Border Background="{Binding Group, Converter={StaticResource ColorConverter}}"/>
Binding to Group gives you access to the group's data source.
I have a simple class which creates a list of objects:
namespace TestWPF2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<TestObj> SomeList { get; set; }
public string WindowTitle { get; set; }
public MainWindow()
{
this.DataContext = this;
WindowTitle = "People";
SomeList = new ObservableCollection<TestObj>();
SomeList.Add(new TestObj("Bob"));
SomeList.Add(new TestObj("Jane"));
SomeList.Add(new TestObj("Mike"));
InitializeComponent();
}
}
}
The TestObj class is as follows:
namespace TestWPF2
{
public class TestObj
{
public string FirstName { get; set; }
public TestObj(string firstName)
{
this.FirstName = firstName;
}
}
}
I then attempt to display each item in the list with the following:
<Window x:Class="TestWPF2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWPF2"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:TestObj}">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Pos: "/>
<TextBlock x:Name="posText"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name: "/>
<TextBlock Text="{Binding FirstName}"/>
</StackPanel>
</StackPanel>
<!-- THESE TRIGGERS DONT WORK -->
<DataTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Text" Value="First" TargetName="posText"/>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Text" Value="Second" TargetName="posText"/>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="3">
<Setter Property="Text" Value="Third" TargetName="posText"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<StackPanel>
<TextBlock Text="{Binding Title}"/>
<ItemsControl HorizontalAlignment="Stretch"
ItemsSource="{Binding SomeList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</Window>
What I would like to display is something like:
Pos: First
Name: Bob
Pos: Second
Name: Jane
etc.
It's pretty straight-forward to bind to the FirstName property of each item in the list, but I would also like bind to the index in the list. I know I can do this from inside an ItemsControl using ItemsControl.AlternationIndex, but how do I link to the AlternationIndex from within in DataTemplate?
You need to understand that your context is TestObj and with your trigger, you are basicly checking the value of a property named ItemsControl which should have a property AlternationIndex.
You should changed the context of your triggers with a RelativeSource binding to the control that hold your object, named the ContentPresenter:
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}, Path=(ItemsControl.AlternationIndex)" Value="0">
<Setter Property="Text" Value="First" TargetName="posText"/>
</Trigger>
<!--- here be the other triggers -->
</DataTemplate.Triggers>
Hope this helps..