How to select checkbox in ListView from code - WPF - wpf

I have a ListView with checkboxes like this:
<ListView
x:Name="usersListView"
Width="300"
ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"
SelectionChanged="childrenListView_SelectionChanged"
Background="{StaticResource BackgroundPrimaryBrush}"
Foreground="{StaticResource WhiteBrush}"
Grid.Row="6" Grid.ColumnSpan="2"
>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Tag="{Binding Id}" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}, Path=IsSelected}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding FullName}" Header="Name" Width="250"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
All checkboxes in ListView are from List 'AllUsers' from database.
Now I want to set specific checkboxes to IsChecked=True in code behind.
I have another List 'Children' which have only few of the 'AllUsers' elements.
What I want is to display ListView with selected checkboxed binded to Persons in 'Children'.
I tried to implement this by myself with INotifyPropertyChanged implemented class wrapper to Person but I couldn't get Binding properly with this.
I hope I did explain the problem properly.
Thank you in advance :)

Consider using a IMultiValueConverter.
In the example below, my Children object is a simple string with the name. I have two list, the AllChildrens list and the SelectedChildrens list.
Foreach element in the AllChildrens collection, the converter checks if the element is contained into SelectedChildrens collection.
XAML: (I've removed the events)
<ListView ItemsSource="{Binding AllChildrens}" Tag="{Binding SelectedChildrens}">
<ListView.Resources>
<local:IEnumerableContainsConverter x:Key="Contains" />
</ListView.Resources>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Content="{Binding}">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource Contains}">
<Binding Path="." />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListView}}" Path="Tag" />
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding FullName}" Header="Name" Width="250"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
ViewModel:
public class Model
{
public Model()
{
AllChildrens = new List<string>()
{
"James",
"Annabelle",
"Kevin",
"William",
"Joseph",
};
SelectedChildrens = new List<string>()
{
"James",
"Annabelle",
"William",
};
}
public List<string> AllChildrens { get; set; }
public List<string> SelectedChildrens { get; set; }
}
Converter:
class IEnumerableContainsConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values != null &&
values.Length == 2 &&
values[0] is string current_children && // Replace with your children object type
values[1] is IEnumerable<string> selected) // Replace with your children object type
{
return selected.Contains(current_children);
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

WPF Column in ListView shall represent a ComboBox when IsFocused = true but a simple TextBox when IsFocused = false

When the cell in the column is in focus the ComboBox shall appear but once the value is selected and the cell is not in focus anymore, only the text shall appear. So the ComboBox shall only be visible when cell is in focus.
This is my code but I've really no clue how to solve that.
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="SchichtID" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="SelectedShiftHID"
SelectedIndex="{Binding SchichtID}"
DisplayMemberPath="Bezeichnung"
ItemsSource="{Binding DataContext.UiShiftHModelList, Mode=OneWay,RelativeSource={RelativeSource AncestorType=ListView},UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
1.EDIT:
What I'm trying here is to put combobox into a column of a ListView. The values published there come from Model A. The DisplayedMemberPath is the description of the row from model a. We save the ID of that row from Model A in Model B. When the data is reloaded the correct description shall be loaded and shown again in the way explained in my initial post.
2.EDIT:
#Anton (that guy deleted his answer?) - your answer doesn't work. It starts that there is no comboBox shown when focussing the cell neither it shows any text.
In the XAML of the View im introducing the converters:
<UserControl.Resources>
<helpers:LastRowVisibilityMultiValueConverter x:Key="LastRowVisibilityMultiValueConverter" />
<helpers:ShiftHIDtoDescriptionConverter x:Key="ShiftHIDtoDescriptionConverter" ShiftH="{Binding DataContext.UiShiftHModelList, Mode=OneWay, ElementName=ShiftT, UpdateSourceTrigger=PropertyChanged}"/>
<helpers:CellTemplateSelector x:Key="cellTemplateSelector" x:Name="cellTemplateSelector">
<helpers:CellTemplateSelector.EditableTemplate>
<DataTemplate>
<ComboBox x:Name="SelectedShiftHID"
SelectedIndex="{Binding ID}"
DisplayMemberPath="Bezeichnung"
ItemsSource="{Binding UiShiftHModelList, Mode=OneWay,ElementName=ShiftT,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</helpers:CellTemplateSelector.EditableTemplate>
<helpers:CellTemplateSelector.ReadOnlyTemplate>
<DataTemplate>
<TextBlock Text="{Binding SchichtID, Converter={StaticResource ShiftHIDtoDescriptionConverter}}"/>
</DataTemplate>
</helpers:CellTemplateSelector.ReadOnlyTemplate>
</helpers:CellTemplateSelector>
</UserControl.Resources>
There simply happens nada.
One error I've got in your suggested converter was:
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(IEnumerable), typeof(ItemIdToStringConverter:DependencyObject), new PropertyMetadata(null));
This here: typeof(ItemIdToStringConverter:DependencyObject)
Following the adjusted converter:
public class ShiftHIDtoDescriptionConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty ShiftHProperty = DependencyProperty.Register("ShiftH", typeof(IEnumerable), typeof(ShiftHIDtoDescriptionConverter),new PropertyMetadata(null));
public IEnumerable ShiftH
{
get { return (IEnumerable)GetValue(ShiftHProperty); }
set { SetValue(ShiftHProperty, value); }
}
public object Convert(object shiftHID, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
int? id = shiftHID as int?;
if (id != null) {
return ShiftH.Cast<UiShiftHModel>().FirstOrDefault(m => m.ID == id)?.Bezeichnung;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
This here is the XAML part:
<Border Grid.Row="1" Grid.Column="1"
Margin="10,10,10,10"
BorderBrush="#FF474A57"
CornerRadius="10,10,10,10"
BorderThickness="2,2,2,2"
Width="520"
MaxHeight="300"
Background="White">
<StackPanel Margin="0,0,0,20" Orientation="Vertical">
<StackPanel Grid.Column="0" Grid.RowSpan="1"
Grid.Row="1"
VerticalAlignment="Top">
<Label HorizontalAlignment="Center" FontWeight="Bold">
Schichtdetails
</Label>
<ListView x:Name="ShiftT" MinHeight="150" MaxHeight="200" MinWidth="500" HorizontalContentAlignment="Stretch" HorizontalAlignment="Center"
ItemContainerStyle="{DynamicResource DifAlternationColorsLV}"
AlternationCount="2"
ItemsSource="{Binding UiShiftTModelList, UpdateSourceTrigger=PropertyChanged}" d:ItemsSource="{d:SampleData ItemCount=5}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="ID" Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="ID" MinWidth="30"
Style="{StaticResource TBoxInListV}"
Text="{Binding ID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
BorderThickness="0">
</TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="SchichtID" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl ContentTemplateSelector="{StaticResource cellTemplateSelector}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</StackPanel>
</Border>
Watching the CellTemplateSelector with a breakpoint shows:
public class CellTemplateSelector : DataTemplateSelector
{
//Answer for question: switch appearance of the ListView column from combobox to textbox
//https://stackoverflow.com/questions/73046926/wpf-column-in-listview-shall-represent-a-combobox-when-isfocused-true-but-a-si/73048416?noredirect=1#comment129022042_73048416
public DataTemplate EditableTemplate { get; set; }
public DataTemplate ReadOnlyTemplate { get; set; }
public override DataTemplate
SelectTemplate(object item, DependencyObject container) {
ContentControl contentControl = container as ContentControl;
if (contentControl != null) {
if (contentControl.IsFocused)
return EditableTemplate;
else
return ReadOnlyTemplate;
}
return null;
}
that the contentControl is always null.
3.EDIT
I guess its not a ContentControl its rather a ContenPresenter. Then the casting works. But now I'm fucked up with Binding Errors:
4.EDIT
Oh, there is another problem with the converter for the id to description. The code therefor from a yet deleted answer is completely bs. The passed id has to be looked up in the UiShiftHModel but there is no chance to pass the collection into the converter. Maybe via multi binding converter....
First of all.. better to create own customControl with properties which allow you to switch templates for Readonly and Editable templates
public class InteractiveContentControl : ContentControl
{
public static readonly DependencyProperty IsEditableProperty =
DependencyProperty.Register("IsEditable", typeof(bool), typeof(InteractiveContentControl), new FrameworkPropertyMetadata(false, OnIsEditablePropertyChanged));
public bool IsEditable
{
get { return (bool)GetValue(IsEditableProperty); }
set { SetValue(IsEditableProperty, value); }
}
private static void OnIsEditablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as InteractiveContentControl;
control.ChangeTemplate();
}
public static readonly DependencyProperty EditableTemplateProperty = DependencyProperty.Register("EditableTemplate", typeof(DataTemplate), typeof(InteractiveContentControl), new PropertyMetadata(null));
public DataTemplate EditableTemplate
{
get { return (DataTemplate)GetValue(EditableTemplateProperty); }
set { SetValue(EditableTemplateProperty, value); }
}
public static readonly DependencyProperty ReadonlyTemplateProperty = DependencyProperty.Register("ReadonlyTemplate", typeof(DataTemplate), typeof(InteractiveContentControl), new PropertyMetadata(null));
public DataTemplate ReadonlyTemplate
{
get { return (DataTemplate)GetValue(ReadonlyTemplateProperty); }
set { SetValue(ReadonlyTemplateProperty, value); }
}
public InteractiveContentControl():base()
{
DefaultStyleKey = typeof(ContentControl);
this.Loaded += OnLoaded;
this.LostFocus += OnLostFocus;
this.IsKeyboardFocusWithinChanged += InteractiveContentControl_IsKeyboardFocusWithinChanged;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
ChangeTemplate();
}
private void InteractiveContentControl_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
IsEditable = IsKeyboardFocusWithin;
}
private void OnLostFocus(object sender, RoutedEventArgs e)
{
IsEditable = IsKeyboardFocusWithin;
}
private void ChangeTemplate()
{
ContentTemplate = IsEditable ? EditableTemplate : ReadonlyTemplate;
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseDown(e);
IsEditable = true;
}
}
Also need to have class for convert Id to the Name from the comboBox.
public class ShiftHIDtoDescriptionConverter : DependencyObject, IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int? id = (int?)values[0];
IEnumerable<UiShiftHModel> items = values[1] as IEnumerable<UiShiftHModel>;
if (id!=null && items!=null)
{
return items.FirstOrDefault(i => i.ID == id)?.Bezeichnung;
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then you can define your resources in xaml
<UserControl.Resources>
<helpers:ShiftHIDtoDescriptionConverter x:Key="ShiftHIDtoDescriptionConverter" />
<DataTemplate x:Key="EditableTemplate" DataType="UIShiftTModel">
<ComboBox x:Name="SelectedShiftHID"
ItemsSource="{Binding DataContext.UiShiftHModelList, Mode=OneWay, ElementName=ShiftT, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="ID"
SelectedValue="{Binding SchichID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Bezeichnung"/>
</DataTemplate>
<DataTemplate x:Key="ReadonlyTemplate" DataType="UIShiftTModel">
<Grid>
<TextBlock HorizontalAlignment="Stretch">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource ShiftHIDtoDescriptionConverter}">
<Binding Path="SchichID" />
<Binding Path="DataContext.UiShiftHModelList" ElementName="ShiftT" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
</UserControl.Resources>
And then you will be able to define your GridView
<ListView x:Name="ShiftT" MinHeight="150"
AlternationCount="2"
ItemsSource="{Binding UiShiftTModelList, UpdateSourceTrigger=PropertyChanged}" d:ItemsSource="{d:SampleData ItemCount=5}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="ID" Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="ID" MinWidth="30"
Text="{Binding ID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="SchichtID" x:Name="colSchichtID">
<GridViewColumn.CellTemplate>
<DataTemplate>
<helpers:InteractiveContentControl Content="{Binding}" Width="200"
EditableTemplate="{StaticResource EditableTemplate}"
ReadonlyTemplate="{StaticResource ReadonlyTemplate}"
/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>

wpf ComboBox binding in list view

I have the problem with binding ComboBox in ListView
In ComboBox I need to bind User object from ListDetails object. If I change ComboBox it work, if I open window, binding don't work.
ComboBoxItem is a full list of all existing user from db.
<GridViewColumn x:Name="colUser" Header="User.Name in ComboBox">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="300" SelectionChanged="ComboBox_SelectionChanged"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MainWindow}}, Path=AlleUser}"
SelectedValue="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}, Path=DataContext.User}"
SelectedValuePath="Key" DisplayMemberPath="Value"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
full code you can see in https://github.com/fialo4ka/ListBinding/tree/master
In example I change db loading to some test data
So... After trying all this answers I still can't see the name of user in loading form, so I found the solution directly put Name to the ComboBox.
but I still can't understand why all previous solution is not working for me :(
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
((ComboBox)sender).Text = ((ListDetails)((ComboBox)sender).DataContext).User.Name;
}
<DataTemplate>
<ComboBox Width="300" SelectionChanged="ComboBox_SelectionChanged" Loaded="ComboBox_Loaded"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MainWindow}}, Path=AlleUser}"
SelectedValue="{Binding User}"
SelectedValuePath="Key" DisplayMemberPath="Key.Value"/>
You should bind to the User property of the ListDetails object:
<ComboBox Width="300" ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MainWindow}}, Path=AlleUser}"
SelectedValue="{Binding User}"
SelectedValuePath="Key" DisplayMemberPath="Key.Name"/>
First, you should never mix data binding and setting values directly in code-behind.It makes debugging and understanding the code much more difficult for everybody, including yourself. Choose one. I highly recommend data binding. I refactored the code to use data binding only.
The below code works for me. Values are displayed in the ComboBox.
If this is still not working as expected, you have to clarify exactly what you are doing, what you are expecting and what is actually happening.
Also you usually don't set the ItemsControl.SelctedValue property. This property holds the result of the ItemsControl.SelectedValuePath, once an item is selected. In your case, when the selected value should be the User.Name value, you would need to set the path to SelectedValuePath="Key.Name" as the Key references the User item of the KeyValuePair and then Name the User.Name property. You would bind the SelectedValue to a property in your MainWindow class, to access it there or to another control, e.g. TextBlock.Text.
When reading new data from the database, you first clear the collections and then add the new items. Since you are using an ObservableCollection the views will update automatically. Don't set the views directly.
MainWindow.xaml
<Window>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListView Name="lvBezirke"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type main:MainWindow}}, Path=ListDetails}"
Grid.ColumnSpan="2">
<ListView.View>
<GridView ColumnHeaderToolTip="zugeordnete User je Bezirk">
<GridViewColumn x:Name="colBezirk" Header="Some Data from model" DisplayMemberBinding="{Binding Bezirk}" />
<GridViewColumn x:Name="colUser" Header="User.Name in ComboBox">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="300" SelectionChanged="ComboBox_SelectionChanged"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type main:MainWindow}}, Path=AlleUser}"
SelectedValuePath="Key" DisplayMemberPath="Value" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="User.Loginname" DisplayMemberBinding="{Binding User.Loginname}" />
<GridViewColumn Header="User.Name" DisplayMemberBinding="{Binding User.Name}" />
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="1" Content="click" Click="Button_Click" />
<Button Grid.Row="1" Grid.Column="1" Content="clear" Click="Button_Click_1" />
<ListView Name="lvBezirkeClick"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type main:MainWindow}}, Path=ListDetailsClick}"
Grid.Row="2" Grid.ColumnSpan="2" >
<ListView.View>
<GridView ColumnHeaderToolTip="zugeordnete User je Bezirk">
<GridViewColumn Header="Some Data from model" DisplayMemberBinding="{Binding Bezirk}" />
<GridViewColumn Header="User.Loginname onClick" DisplayMemberBinding="{Binding User.Loginname}" />
<GridViewColumn Header="User.Name onClick" DisplayMemberBinding="{Binding User.Name}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
MainWindow.xaml.cs
partial class MainWindow
{
public ObservableCollection<ListDetails> ListDetails { get; set; }
public ObservableCollection<ListDetails> ListDetailsClick { get; set; }
public ObservableCollection<KeyValuePair<User, string>> AlleUser { get; set; }
public MainWindow()
{
InitializeComponent(); // Always the first line in constructor!
this.ListDetailsClick = new ObservableCollection<ListDetails>();
// Initialize ObservableCollection directly
this.ListDetails = new ObservableCollection<ListDetails>()
{
new ListDetails("data 1", new User("1 user", "1")),
new ListDetails("data 2", new User("8 user", "8")),
new ListDetails("data 3", new User("5 user", "5")),
};
this.AlleUser = new ObservableCollection<KeyValuePair<User, string>>()
{
{ new KeyValuePair<User, string>(new User("1 user", "1"), "1") },
{ new KeyValuePair<User, string>(new User("2 user", "2"), "2") },
{ new KeyValuePair<User, string>(new User("4 user", "3"), "3") },
{ new KeyValuePair<User, string>(new User("4 user", "4"), "4") },
{ new KeyValuePair<User, string>(new User("5 user", "5"), "5") },
{ new KeyValuePair<User, string>(new User("6 user", "6"), "6") },
{ new KeyValuePair<User, string>(new User("7 user", "7"), "7") },
{ new KeyValuePair<User, string>(new User("8 user", "8"), "8") },
};
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
// Clear and copy
private void Button_Click(object sender, RoutedEventArgs e)
{
this.ListDetailsClick.Clear();
this.ListDetails.ToList().ForEach(this.ListDetailsClick.Add);
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.ListDetailsClick.Clear();
}
}

How to get current cell value in GridView binding

Having spent the last few hours with this rather simple problem, I haven't yet found a way to do it. I'm using a GridView to display some data and need to perform some customization of the cell values using a converter. Here is the relevant XAML:
<ListView ItemsSource="{Binding SomeDataTable}">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=XXX, Converter={StaticResource MyConverter}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
What do I need to write in place of XXX above?
N.B. I cannot put constant column name here because this DataTemplate is to be used by all columns of GridView. I need a way to refer to current cell value just like how GridView's default template works when using DisplayMemberBinding.
I found a relevant post here, but that also doesn't have any working answer.
One option is to use DisplayMemberBinding to create the GridViewColumns and then to define your TextBlock's style as a resource of your ListView and to use a converter there.
Given a scenario where I have a list of persons and I want to color all the cells based on the person's age:
Data item:
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public Person(int age, string name)
{
Age = age;
Name = name;
}
}
Creating the columns in XAML:
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Age}" />
<GridViewColumn DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
The converter which changes cell background based on the age:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((int) value < 20)
{
return Brushes.LightBlue;
}
return Brushes.LightGreen;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
Using the converter:
<ListView.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Background">
<Setter.Value>
<Binding Path="Age">
<Binding.Converter>
<local:MyConverter/>
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
</Style>
</ListView.Resources>
Result:
Full XAML:
<ListView x:Name="MyItems" Loaded="MyItems_OnLoaded">
<ListView.Resources>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Background">
<Setter.Value>
<Binding Path="Age">
<Binding.Converter>
<local:MyConverter/>
</Binding.Converter>
</Binding>
</Setter.Value>
</Setter>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Age}" />
<GridViewColumn DisplayMemberBinding="{Binding Name}" />
</GridView>
</ListView.View>
</ListView>

How to Bind on a Property by String as Propertyname

Hi i want to Bind to an "unknown" (i only get a string) Property in Xaml
at first i wrote an IValueConverter but you can't bind to ConverterParameter
so i rewrite it as IMultiValueConverter but now i'm unable to figure out how to use the <Binding /> with out Path
or my i'm wrong?
if you write <TextBlock Text="{Binding}" /> you will get the object Person
and with {Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=View.Columns[0].Header}} i'm able to access the Header Text of the first row
now i'm only need to combine both and a will get the Property right?
my test Xaml code:
<UserControl x:Class="Frameworktest.View.auswahl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Frameworktest">
<UserControl.Resources>
<local:toPropertyConverter x:Key="PropertyConverter"/>
</UserControl.Resources>
<StackPanel>
<!--...-->
<Border BorderThickness="5" HorizontalAlignment="Left" VerticalAlignment="Top"
BorderBrush="Green" CornerRadius="5">
<ListView Name="listView1" IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Items, UpdateSourceTrigger=PropertyChanged}" <!--ObservableCollection<Person>-->
SelectedItem="{Binding selectedItem, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="1">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource PropertyConverter}">
<Binding /><!-- How do i {Binding} here?-->
<Binding Source="{Binding RelativeSource={Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=View.Columns[0].Header}}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Firstname" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="1" Text="{Binding Path=Name}" Width="100"/><!--works-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Age">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="1" Text="{Binding Age}" Width="50"/><!--works-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Border>
</StackPanel>
</UserControl>
the Converter:
public class toPropertyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].GetType().GetProperty((string)values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The Model
public class Person : MBase, IContains
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value;
RaisePropertyChanged(() => Reg(() => Name));
}
}
private string _firstname;
public string Firstname
{
get { return _firstname; }
set
{
_firstname = value;
RaisePropertyChanged(() => Reg(() => Firstname));
}
}
private int _age;
public int Age
{
get { return _age; }
set
{
_age = value;
RaisePropertyChanged(() => Reg(() => Age));
}
}
public bool Contains(string text)
{
string pers = string.Format("{0} {1}", Firstname, Name);
return pers.Contains(text);
}
}
Update my current Multibindung
<MultiBinding Converter="{StaticResource PropertyConverter}">
<Binding Path="."/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}" Path="View.Columns[0].Header}}" /> <!-- doesn't contain the word "Name" like i suspected -->
</MultiBinding>
LAST Update
it is a dead end in my case you can't Bind from the GridViewColumn.CellTemplate to the specific Column Header Value
{Binding} implicitely means : {Binding Path=.}.
So you could use
<Binding Path="."/>

Localize currency values in WPF GridView with different cultures for each row based on XAML bindings to culture name property

First time poster here but have been reading SO for ages and finally have run into a question that I've not been able to answer.
I've got a ListView hosting a GridView with multiple columns. One displays a price and another displays a currency code (CAD, USD, GBP, etc). This is all pulled out of SQL server using Entity Framework so the GridView is databound to a IEnumerable which stores the result of my query. The currency code is stored in a separate table with a localization string (en-US, en-GB) which (in a WinForms version of this app) was previously used in String.Format() to localize the currency to display the appropriate currency format and symbol.
The problem I have is in XAML binding the ConverterCulture of the Price binding to the Currency.LocalizedCultureName to get it to format correctly. Here's my current XAML:
<ListView Grid.Column="0" Name="pricingListingListView" ItemsSource="{Binding Source={StaticResource pricesByYear}}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<GroupBox Header="{Binding Name}" Margin="0,0,0,10">
<ItemsPresenter/>
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Date" DisplayMemberBinding="{Binding Source.Date}" Width="60" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Price" DisplayMemberBinding="{Binding Price, StringFormat='{}{0:C}', ConverterCulture={Binding Currency.LocalizedCultureName}}" Width="60" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Currency" DisplayMemberBinding="{Binding Currency.Code}" Width="60" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Unit" DisplayMemberBinding="{Binding Unit.Name}" Width="60" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Source" DisplayMemberBinding="{Binding Source.Name}" Width="125" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Project" DisplayMemberBinding="{Binding Project.Description}" Width="125" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Plant Type" DisplayMemberBinding="{Binding Project.Plant.Name}" Width="100" />
</GridView>
</ListView.View>
</ListView>
PricesByYear is simply a CollectionViewSource which pulls the IEnumerable out of a DP in my code behind. The data is pulled out correctly, just not formatted.
This compiles fine, but generates a XamlParseException when I load the window containing it: A 'Binding' cannot be set on the 'ConverterCulture' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
The line generating the error is: <GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Price" DisplayMemberBinding="{Binding Price, StringFormat='{}{0:C}', ConverterCulture={Binding Currency.LocalizedCultureName}}" Width="60" />
The short-form objective is to display the price, but format it according to the culture name stored as a string value. Each row in the gridview could potentially be different. As it seems I cannot bind within a binding, is there an alternative way I could go about this?
Answer
Multibinding did the trick, here's the working XAML:
<local:LocalizeCurrencyMultiConverter x:Key="localizeCurrencyMultiConverter"/>
...
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Price" Width="60">
<!--DisplayMemberBinding="{Binding Price, StringFormat='{}{0:C}', ConverterCulture={Binding Currency.LocalizedCultureName}}"-->
<GridViewColumn.DisplayMemberBinding>
<MultiBinding Converter="{StaticResource localizeCurrencyMultiConverter}">
<Binding Path="Price"/>
<Binding Path="Currency.LocalizedCultureName"/>
</MultiBinding>
</GridViewColumn.DisplayMemberBinding>
</GridViewColumn>
And the converter class:
public class LocalizeCurrencyMultiConverter :System.Windows.Data.IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
string localizedCurrency;
if (!values.Any() || values[0] == null)
throw new ArgumentException("Convert requires a minimum a price to display, and optionally a culture.");
double originalCurrency;
if (!double.TryParse(values[0].ToString(), out originalCurrency))
return values[0];
string localization = (values[1] ?? "en-CA").ToString();
try {
localizedCurrency = string.Format(System.Globalization.CultureInfo.CreateSpecificCulture(localization), "{0:c}", originalCurrency);
} catch {
localizedCurrency = string.Format(System.Globalization.CultureInfo.CreateSpecificCulture("en-CA"), "{0:c}", originalCurrency);
}
return localizedCurrency;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
return null;
}
}
Works like a charm.
Use a MultiBinding with a binding to the Price property and a binding the Culture property,
Write a MultiValueConverter and use the values to output the string you want.
I am going to make it easy for you:
MSDN MultiBinding
MSDN MultiConverter

Resources