UWP Bind Listview items foreground to property - c#

I want to change the foreground of certain items in a ListView based on a property of such items. If the item has the property "EsBlacklist" set to true, its foreground should be red.
<Page.Resources>
<converter:ForegroundColorConverter x:Key="ForegroundConverter" x:Name="ForegroundConverter"/>
</Page.Resources>
<StackPanel Grid.Column="1" Grid.Row="1">
<TextBlock HorizontalAlignment="Center" Margin="10" FontSize="24">VehĂ­culos sin VTV</TextBlock>
<ListView ItemsSource="{x:Bind ViewModel.PatentesSinVtv}" Margin="10" DisplayMemberPath="Placa"
SelectedItem="{x:Bind ViewModel.PatenteSeleccionada, Mode=TwoWay}"
HorizontalAlignment="Center"
IsItemClickEnabled="False"
IsSwipeEnabled="False"
CanDragItems="False"
SelectionMode="Single"
Grid.Column="1"
Grid.Row="1">
<ListViewItem Foreground="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource ForegroundConverter}}"></ListViewItem>
( Self should reference the item and not ListViewItem.)
</ListView>
</StackPanel>
And the converter:
class ForegroundColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var patente = (Patente)value; //value is not a Patente but ListViewItem
return patente.EsBlacklist ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Gray);
}
}
My problem is that "value" received in the converter is not a Patente but a ListViewItem

My problem is that "value" received in the converter is not a Patente but a ListViewItem
As the document of {RelativeSource} markup extension,
{RelativeSource Self} Produces a Mode value of Self. The target element should be used as the source for this binding. This is useful for binding one property of an element to another property on the same element.
...
The Self mode is useful for binding one property of an element to another property on the same element, and is a variation on ElementName binding but does not require naming and then self-referencing the element.
Here is an example to use the RelativeSource={RelativeSource Self},
<Rectangle
Fill="Orange" Width="200"
Height="{Binding RelativeSource={RelativeSource Self}, Path=Width}"/>
You can see the document to learn how to use {RelativeSource} markup extension
You can bind the ViewModel directly to make the converter be the Patente,
<ListViewItem Foreground="{x:Bind ViewModel, Mode=TwoWay, Converter={StaticResource ForegroundConverter}}"/>

The value of foreground color is not a plain color, but a brush.
So your converter should return new SolidColorBrush(Colors.Red).

You may deal with it like this:
<ListViewItem>
<ListViewItem.Foreground>
<SolidColorBrush Color="{x:Bind YourColor}"/>
</ListViewItem.Foreground>
</ListViewItem>

I needed to implement an ItemTemplate
<ListView.ItemTemplate>
<DataTemplate x:DataType="modelo:Patente">
<TextBlock Text="{Binding Placa}" Foreground="{x:Bind EsBlacklist, Mode=TwoWay, Converter={StaticResource ForegroundConverter}}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
and the converter becomes this:
public object Convert(object value, Type targetType, object parameter, string language)
{
return (bool)value? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Gray);
}

Related

how can I change the color of my text while I type in the text box by using the colors in combo box?

I created a Combo Box which includes all colors in Colors class. I'd like to change TextBox's foreground color by selecting the color from ComboBox. How can I do that?
And can you also explain the logic in {Binding Name}, I did not understand why I used Name keyword but it worked.
<StackPanel>
<ComboBox Name="CBox" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Width="20" Height="20" Fill="{Binding Name}"/>
<TextBlock Text="{Binding Name}" Margin="5" FontSize="20"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox x:Name="tBox"/>
</StackPanel>
public MainWindow()
{
InitializeComponent();
CBox.ItemsSource = typeof(Colors).GetProperties();
}
I tried this approach and it gave error
tBox.Foreground = (Colors)CBox.SelectedItem;
Edit : The main problem was binding TextBox.
I simply fixed problem by changing TextBox code to this :
<TextBox Foreground="{Binding ElementName=CBox, Path=SelectedItem.Name}"/>
but Clemens' approach is better so I proceed to use it.
I would suggest to assign a collection of anonymous objects with a Name and a Brush property to the ItemsSource of the ComboBox. The code below uses all the public static Brush properties of the Brushes class, instead of the Color properties of the Colors class.
The expression below first gets all public static properties of the System.Windows.Media.Brushes class, similar to what you already did for the Colors class. Then it maps this collection of PropertyInfo objects to a collection of instances of an anonymous class with a Name and a Brush property, by means of the Enumerable.Select method. The Brush is created by calling the GetValue method of each PropertyInfo.
CBox.ItemsSource = typeof(Brushes)
.GetProperties(BindingFlags.Static | BindingFlags.Public)
.Select(p => new { Name = p.Name, Brush = p.GetValue(null) });
Then bind this data like shown below. The Foreground property of the TextBox is bound to the Brush property of the selected item, i.e. the selected instance of the anonymous class.
<ComboBox Name="CBox" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Width="20" Height="20" Fill="{Binding Brush}"/>
<TextBlock Text="{Binding Name}" Margin="5"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Foreground="{Binding SelectedItem.Brush, ElementName=CBox}"/>

Binding in GroupStyle header not updated when collection changes

I have an ItemsControl which is bound to a CollectionViewSource bound to a property on the View Model.
The ItemsControl has a GroupStyle set which looks like this:
<GroupStyle HeaderTemplate="{StaticResource TotalDurationTemplate}" />
Where TotalDurationTemplate is:
<DataTemplate x:Key="TotalDurationTemplate">
<Border BorderBrush="Black" BorderThickness="0 1" Background="#EEE">
<Grid>
<TextBlock HorizontalAlignment="Center"
FontSize="18" FontWeight="Bold"
Text="{Binding Path=Items[0].Start, Converter={StaticResource DateTimeFormatConverter}, ConverterParameter='ddd dd/MM'}" />
<TextBlock Margin="10 0" HorizontalAlignment="Right" VerticalAlignment="Center"
FontSize="16" Foreground="#9000"
Text="{Binding Items, Converter={StaticResource TotalDurationConverter}}" />
</Grid>
</Border>
</DataTemplate>
The issue is that the second TextBlock (the one bound to Items) is not re-evaluated when a new item is added to the View Model's collection (which is an ObservableCollection<>). The item is added to the ListView into the correct group but the Total Duration value is not updated.
The Converter for Total Duration looks like this:
public class TotalDurationConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return
((IEnumerable<object>)value)
.Select(x => ((RecentTimingViewModel)x).Duration)
.Aggregate((v1, v2) => v1 + v2)
.TotalHours
.ToString("F2") + "h";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
How do I make the binding refresh correctly when the items in the View Model are changed?
EDIT: The Solution
I took Solution 2 from the Accepted Answer and put it into my code. This is what ended up working:
<DataTemplate x:Key="TotalDurationTemplate">
<Border BorderBrush="Black" BorderThickness="0 1" Background="#EEE">
<Grid>
<TextBlock HorizontalAlignment="Center"
FontSize="18" FontWeight="Bold"
Text="{Binding Path=Items[0].Start, Converter={StaticResource FormatDateIntelligentConverter}}" />
<TextBlock Margin="10 0" HorizontalAlignment="Right" VerticalAlignment="Center"
FontSize="16" Foreground="#9000">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TotalDurationConverter}">
<MultiBinding.Bindings>
<Binding Path="Items" />
<Binding Path="Items.Count" />
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Border>
</DataTemplate>
And changing TotalDurationConverter to IMultiValueConverter. The just ignore the second item in the Array.
So two possibilities and if you can try below simple solutions and let me know if it works.
Solution 1 - a very simple and basic one since you are using a textbloxk set the mode explicitly to Two way. I guess TextBlock default binding mode is One way.
Solution 2 - I have faced similar issue working with a combo box- here is a work around which worked for me
For the second Text block use Multi Binding, first bind it to List as you have already done, second bind it to any property in View Model which will be triggered when your list is getting changed (example an int property returning List.Count) - This second dummy property will make sure that your converter is re evaluated.
I guess second option should work for you.
Let me know if it doesn't work.
regards,
Vishal

How to apply a style of a Button dynamically in WP 8.1?

I have one WP 8.1 apps. I am following a complete MVVM design pattern. In the XAML code
<Grid>
<Canvas x:Name="gameCanvas"
Width="{Binding CanvasWidth,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
<ItemsControl ItemsSource="{Binding Path=ButtonItems,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate x:Name="Dta">
<Button x:Name="newsItemBtn"
Height="{Binding Height}"
Width="{Binding Width}"
Style="{StaticResource ButtonStyle2}"
Content="{Binding Content}"
Command="{Binding
Path=DataContext.ButtonClickCommand,
ElementName=gameCanvas}"
CommandParameter="{Binding Content}">
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
But what I want is to assign the style of the button dynamically at runtime.
I have three Button Style defined in the GamePage.XAML.
<Style x:Key="ButtonStyle1" />
<Style x:Key="ButtonStyle2" />
<Style x:Key="ButtonStyle3" />
Now based on some value or some property on ViewModel i want to select the button Style
I tried using a converter. But not much success.
Any idea how can I achieve this?
At one point I needed to do the same thing and I just used a converter, that returned a named resource:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool && (bool)value)
{
return App.Current.Resources["ComplexProductTypeTemplate"];
}
return App.Current.Resources["SimpleProductTypeTemplate"];
}
I guess it could work the same way with resources identified by keys.

How do I Set height of a grid inside a DataTemplate

The DataTemplate is used in an ItemsControl inside a UserControl. The UserControl is added multiple times inside a stackpanel. (pfew)
I need to be able to determine how many children the stackpanel has. I assumed this was possible using the FindAncestor mode, but I'm afraid I need your help.
Here's the XAML logic:
<StackPanel Name="BeforeTournament" Orientation="Horizontal" VerticalAlignment="Top">
<UserControl ...
<Grid>
<TextBlock Name="txtTitle" FontSize="14" />
<ItemsControl Name="MatchList" ItemsSource="{Binding Matches, Mode=OneWay}" Width="400" Margin="-7,20,0,0"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Name="MatchTemplate" Width="390"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}},
Path=(Children.Count * 300}"
Margin="0,0,0,50" VerticalAlignment="Center">
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
//Duplicates below, same logic to determine width
<UserControl></UserControl>
</StackPanel>
So I would basically like to know how many UserControls have been added to the stackpanel and be able to use this amound of children to calculate the height of the grid inside the DataTemplate.
The FindAncestor relative source is giving me an error saying that Children is not supported in a relative context.
Ok, as I said in the comment there should be a better way to do this, but I'am pretty sure one way to do this is to use a converter. Pass the stackpanel as parameter and return the number of children multiplied by 300(If that is what you want)
I have tried this code and it works. Just for show i added two usercontrols manually. I have also tried putting the usercontrols in a seperate xaml file.
Main.xaml
<Window.Resources>
<local:StackpanelConverter x:Key="StackpanelConverter"/>
</Window.Resources>
<StackPanel Name="BeforeTournament" Orientation="Horizontal" VerticalAlignment="Top">
<UserControl>
<Grid Height="200" Background="Brown">
<TextBlock Name="txtTitle" FontSize="14" />
<ItemsControl Name="MatchList" ItemsSource="{Binding MyControls}" BorderBrush="Bisque" BorderThickness="10" Width="400" Margin="-7,20,0,0"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Name="MatchTemplate" Width="390" Background="Blue"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Converter={StaticResource StackpanelConverter}}"
Margin="0,0,0,50" VerticalAlignment="Center">
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
<UserControl>
<Grid Height="200" Background="Brown">
<TextBlock FontSize="14" />
<ItemsControl ItemsSource="{Binding MyControls}" BorderBrush="Bisque" BorderThickness="10" Width="400" Margin="-7,20,0,0"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Name="MatchTemplate" Width="390" Background="Blue"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Converter={StaticResource StackpanelConverter}}"
Margin="0,0,0,50" VerticalAlignment="Center">
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
</StackPanel>
Example of converter: (this is written in notepad so there might be errors)
public class StackpanelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var stackpanel = value as StackPanel;
var height = stackpanel.Children.Count;
return height*300;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Tell me if I still don't understand the question :)

trouble with changing an item's property in a listbox

I've created a listbox, to which i can add and delete items dynamically UI changes accordingly and it works fine.
<ListBox Name="MsgsList" ItemsSource="{Binding Items}" Style="{StaticResource MsgsBoxStyle}">
<ListBox.ItemTemplate>
<DataTemplate x:Name="MsgsDataTemplate">
<StackPanel Tag="{Binding MsgTagInfo}" ManipulationCompleted="StackPanel_Msgs_ManipulationCompleted">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Hold="GestureListener_Hold" Tap="GestureListener_Tap"/>
</toolkit:GestureService.GestureListener>
<Grid x:Name="ContentPanelInner" Grid.Row="1" Width="500">
<StackPanel x:Name="stackPanelInner" Width="500">
<Grid VerticalAlignment="Top" Width="500">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding MsgTitle}" Style="{StaticResource MsgLine1}" />
<TextBlock Grid.Column="1" Text="{Binding MsgDate}" Style="{StaticResource MsgDate}" />
</Grid>
<TextBlock Text="{Binding MsgBody}" Style="{StaticResource MsgLine2}" />
</StackPanel>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
but i didn't understand how to change a style for a particular item's textblock, say based on some condition, if i want to change color of specific item's textbox(s), not sure how to access that.
can somebody please help me with this? thanks.
Probably not the easiest way to do this, but arguably the cleanest from a separation of concerns point of view is by using a converter, and binding that to the property you want to monitor...
For example, if your model is changing state based on a boolean property called myProperty, you could use something like this.
<StackPanel Background={Binding myProperty, Converter={StaticResource myBindingConverter}" />
Your converter should return a SolidColorBrush, based on the value of your property.
public class AlternateRowColour : IValueConverter
{
SolidColorBrush normal = new SolidColorBrush(Colors.Transparent);
SolidColorBrush highlighted = new SolidColorBrush(Color.FromArgb(255, 241, 241, 241));
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var myValue = (bool)value
return myValue ? highlighted : normal ;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
I If you just want to change on aspect of an items style, for example its color, you could expose that as property of the model object you are binding. For example, add a property TextColor and bind it as follows:
<TextBlock Text="{Binding MsgBody}" Style="{StaticResource MsgLine2}">
<TextBlock.Color>
<SolidColorBrush Color="{Binding TextColor}"/>
</TextBlock.Color>
</TextBlock>
This will take precedence over the colour defined via the style.

Categories

Resources