ValueConverter for Background Color - wpf

I have a Forecast class. One of the field's type is Enum:
enum GeneralForecast
{
Sunny,
Rainy,
Snowy,
Cloudy,
Dry
}
class Forecast
{
public GeneralForecast GeneralForecast { get; set; }
public double TemperatureHigh { get; set; }
public double TemperatureLow { get; set; }
public double Percipitation { get; set; }
}
I display list of forecasts on the ListBox and I want to set the BackgroundColor of the item in the ListBox depend on GeneralForecast.
So I have created Converter:
class GeneralForecastToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var gf = (GeneralForecast) value;
switch (gf)
{
case GeneralForecast.Cloudy:
return "FF1D1D1D";
case GeneralForecast.Dry:
return "55112233";
case GeneralForecast.Rainy:
return "88FF5522";
case GeneralForecast.Snowy:
return "9955FF22";
case GeneralForecast.Sunny:
return "FF11FF99";
}
return "FFFFFFFF";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Here is my XAML:
<Page.Resources>
<local:GeneralForecastToBrushConverter x:Key="gf2color"/>
</Page.Resources>
<ListBox ItemsSource="{Binding}" Grid.Row="2" HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="4" BorderBrush="Black" Padding="4"
BorderThickness="2"
Background="{Binding GeneralForecast, Converter={StaticResource gf2color}}">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="20" FontWeight="Bold"
Text="{Binding GeneralForecast}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If I debug my Converter I can see that it returns different colors, but I have all items the same color. Why ?

When you write something like this in XAML:
<Button Background="#FF11111" />
The Xaml parser will convert that string to its equivalent color at runtime.
But when you assign color in C# somehow, you may not set the color as string.
Instead you should use something like SolidColorBrush instances.
So return some variable which is Brush, such as solid or gradient color brushes.
Let me know if any other information is needed.

Related

Visibility binding

I have this situation:
<TextBlock x:Name="NoMonthDataTextBlock"
Text="No data."
Margin="20,10,0,0"
Foreground="Black"
FontWeight="Bold"
FontSize="20"
Visibility="{Binding SelectedSymbolItem.NoData, Converter={StaticResource FieldVisible}}"/>
<tools:BoolToVisibilityConverter x:Key="FieldVisible" TrueValue="Visible" FalseValue="Collapsed" />
public class BoolToVisibilityConverter : BoolToValueConverter<Visibility>
{
}
public class BoolToValueConverter<T> : IValueConverter
{
public T FalseValue { get; set; }
public T TrueValue { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return FalseValue;
else
return (bool)value ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value != null ? value.Equals(TrueValue) : false;
}
}
The problem is when the SelectedSymbolItem is null the field is visible and I don't want that.
I want this textBlock to be visible only when the SelectedSymbolItem is not null and has empty data.
I have thought about using Multibing but I'm targeting windows store apps(8.0) and is not supported here. (one condition for not null and another for no data.)
How can I make the textBlock to be Collapsed when SelectedSymbolItem is null?
Put a FallbackValue of Collapsed ,
in your case this would work there would be a Binding error on the Visibility Property and it will take the supplied FallbackValue.
<TextBlock x:Name="NoMonthDataTextBlock"
Text="No data."
Margin="20,10,0,0"
Foreground="Black"
FontWeight="Bold"
FontSize="20"
Visibility="{Binding SelectedSymbolItem.NoData,Converter={StaticResource FieldVisible},FallbackValue=Collapsed}"/>
<tools:BoolToVisibilityConverter x:Key="FieldVisible" TrueValue="Visible" FalseValue="Collapsed" />

IValueConverter not working for SolidColorBrush

I have a progress bar that I want to change color depending on a boolean value; true is green and false is red. I have code that seems like it should work (it returns the correct value when I bind it to a textbox) but not when it's the color property of the progress bar. The converter is defined as this (in App.xaml.cs since I want to access it anywhere):
public class ProgressBarConverter : System.Windows.Data.IValueConverter
{
public object Convert(
object o,
Type type,
object parameter,
System.Globalization.CultureInfo culture)
{
if (o == null)
return null;
else
//return (bool)o ? new SolidColorBrush(Colors.Red) :
// new SolidColorBrush(Colors.Green);
return (bool)o ? Colors.Red : Colors.Green;
}
public object ConvertBack(
object o,
Type type,
object parameter,
System.Globalization.CultureInfo culture)
{
return null;
}
}
I then add the following to the App.xaml (so it can be a global resource):
<Application.Resources>
<local:ProgressBarConverter x:Key="progressBarConverter" />
<DataTemplate x:Key="ItemTemplate">
<StackPanel>
<TextBlock Text="{Binding name}" Width="280" />
<TextBlock Text="{Binding isNeeded,
Converter={StaticResource progressBarConverter}}" />
<ProgressBar>
<ProgressBar.Foreground>
<SolidColorBrush Color="{Binding isNeeded,
Converter={StaticResource progressBarConverter}}" />
</ProgressBar.Foreground>
<ProgressBar.Background>
<SolidColorBrush Color="{StaticResource PhoneBorderColor}"/>
</ProgressBar.Background>
</ProgressBar>
</StackPanel>
</DataTemplate>
</Application.Resources>
I added the following to MainPage.xaml to display them:
<Grid x:Name="LayoutRoot" Background="Transparent">
<ListBox x:Name="listBox"
ItemTemplate="{StaticResource ItemTemplate}"/>
</Grid>
And then in MainPage.xaml.cs, I define a class to hold the data and bind it to the listBox:
namespace PhoneApp1
{
public class TestClass
{
public bool isNeeded { get; set; }
public string name { get; set; }
}
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
var list = new LinkedList<TestClass>();
list.AddFirst(
new TestClass {
isNeeded = true, name = "should be green" });
list.AddFirst(
new TestClass {
isNeeded = false, name = "should be red" });
listBox.ItemsSource = list;
}
}
}
I've attached a minimal working example so it can just be built and tested. An image of the output is here:
It returns the values from the converter for the textbox but not the progress bar. When I run the debugger, it doesn't even call it.
Thanks for any help!
Try to modify your converter to return a SolidColorBrush and then bind directly to your ProgressBars Foreground property.

When is ValidationSummary loaded?

I'm trying to realize when the ValidationSummary really loads and how can I force it to be loaded.
I've suscribed to loaded event to force a page validation and this is only triggered when I cause any "new" validation or open a ComboBox or something like this.
Any idea?? Thanks in advance.
Here goes my view:
<Grid Margin="0,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"></ColumnDefinition>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<sdk:Label Grid.Column="0" Target="{Binding ElementName=txtImporteTotal}" Content="Total Acto" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5,0"></sdk:Label>
<TextBox Grid.Column="1" Name="txtImporteTotal" Margin="5,0" VerticalAlignment="Center" HorizontalAlignment="Stretch" Text="{Binding ActoMedico.importe_total, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"></TextBox>
<sdk:DescriptionViewer Target="{Binding ElementName=txtImporteTotal}" Grid.Column="2"></sdk:DescriptionViewer>
<sdk:Label Grid.Column="3" Target="{Binding ElementName=txtImporteMedico}" Content="Total Médico" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5,0"></sdk:Label>
<TextBox Grid.Column="4" Name="txtImporteMedico" Margin="5,0" VerticalAlignment="Center" HorizontalAlignment="Stretch" Text="{Binding ActoMedico.importe_medico, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"></TextBox>
<sdk:DescriptionViewer Target="{Binding ElementName=txtImporteMedico}" Grid.Column="5"></sdk:DescriptionViewer>
This is its code behind, where I'm forcing the validation:
public ActoMedico()
{
InitializeComponent();
this.validationSummary.Loaded += new RoutedEventHandler(validationSummary_Loaded);
}
void validationSummary_Loaded(object sender, RoutedEventArgs e)
{
this.forzarValidacion();
}
private void forzarValidacion()
{
this.txtImporteMedico.GetBindingExpression(TextBox.TextProperty).UpdateSource();
this.txtImporteTotal.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
And finally, this is the model:
#region importe_medico
public const string importe_medicoPropertyName = "importe_medico";
private double? _importe_medico;
[Display(Description = "Importe")]
[Required(ErrorMessage = "Debe indicar el importe")]
public double? importe_medico
{
get
{
return _importe_medico;
}
set
{
Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = importe_medicoPropertyName });
_importe_medico = value;
RaisePropertyChanged(importe_medicoPropertyName);
}
}
#endregion
#region importe_total
public const string importe_totalPropertyName = "importe_total";
private double? _importe_total;
[Display(Description = "Importe total")]
[Required(ErrorMessage = "Debe indicar el importe total")]
public double? importe_total
{
get
{
return _importe_total;
}
set
{
Validator.ValidateProperty(value, new ValidationContext(this, null, null) { MemberName = importe_totalPropertyName });
_importe_total = value;
RaisePropertyChanged(importe_totalPropertyName);
}
}
#endregion
As far as I see, you use the binding like {Binding ActoMedico.importe_total} in your text boxes. It means that the object bound to the UserControl must contain the property ActoMedico and the value of this property must contain the inner property importe_total.
I've added the following code to the code behind:
public class MainViewModel
{
public MainViewModel()
{
this.ActoMedico = new ItemViewModel();
}
public ItemViewModel ActoMedico { get; set; }
}
//...
public ActoMedico()
{
InitializeComponent();
this.DataContext = new MainViewModel(); //should be set in order to make bindings work
this.validationSummary.Loaded += new RoutedEventHandler(validationSummary_Loaded);
}
In the code above the ItemViewModel class is the class which contains those two properties from you last block of code. This class has a different name in your application, but I don't know it so I've named it in my way.
So now the ValidationSummary control will be displayed but with messages like Input string had an incorrect format. It is because the TextBox control expects the string data type whereas your view model has the double? data type. You can write a converter as a workaround to this issue:
public class DoubleToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var str = (string)value;
double d;
if (!double.TryParse(str, out d))
return null;
return d;
}
}
Usage:
<UserControl.Resources>
<local:DoubleToStringConverter x:Key="DoubleToStringConverter" />
</UserControl.Resources>
<!-- ... -->
<TextBox Text="{Binding ActoMedico.importe_total, Mode=TwoWay, Converter={StaticResource DoubleToStringConverter} ...
Anyway data annotations aren't easy to work with, so I reccomend to use the MVVM pattern and the INotifyDataErrorInfo interface. I've implemented the example of such validation here in my post, you can download source code and look how it is implemented.
Finally I get this by forcing a combobox to drop-down, force all the form validations and then undo the drop-down.
So I get the ValidationSummary from the beginning (I want every error from the start to be shown and disable the "save" button for the user.
Thanks so much for your help #vorrtex.

How to bind a list of child objects in Silverlight MVVM

In Silverlight, MVVM I have to create a property window, but I have just a
List<AProperty> object, where AProperty is an abstract class with some child classes.
I want to bind it to a Silverlight control (but to which one?), with some conditions:
some child-properties must be shown as a textbox, some as a checkbox, and some as a combobox. It comes from the dynamic type.
the AProperty class has a PropertyGroup and a Name field. The order must be alphabetic (PropertyGroup > Name)
Any idea or working example?
My code:
public abstract class AProperty {
private String _Name;
private String _Caption;
private String _PropertyGroup;
public String Name {
get { return _Name; }
set { _Name = value; }
}
public String Caption {
get { return _Caption; }
set { _Caption = value; }
}
public String PropertyGroup {
get { return _PropertyGroup; }
set { _PropertyGroup = value; }
}
}
List<AProperty> Properties;
And the xaml:
<ListBox ItemsSource="{Binding ... Properties ...}">
<!-- Here order the items -->
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="250"
Text="{Binding Path=Caption}" />
<!-- And here need I a textbox, or a checkbox, or a combobox anyway -->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I've found value converters just for a control attribute, not for a whole stackpanel content.
You could use a value converter to check what Type the object is and return the correct control type.
Do you have any sample code so I could give a better example?
The ValueConverter Andy mentions should work. Underneath your Textblock have a ContentPresenter, and bind it's Content.
Content={Binding , ConverterParameter={StaticResource NAMEOFCONVERTER}}.
The binding to the property is empty since you already have the object you want as the context. Then just check the type and return the control you want. you may want to make usercontrol's that are just Textblocks, etc. so that you can set the binding there, otherwise you will have to do it in code.
Thanks Jesse and Andy, here is the solution
The converter:
public class PropertyConverter : IValueConverter {
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
Binding ValueBinding = new Binding() {
Source = value,
Path = new PropertyPath( "Value" ),
Mode = BindingMode.TwoWay
};
Binding EditableBinding = new Binding() {
Source = value,
Path = new PropertyPath( "Editable" ),
Mode = BindingMode.TwoWay
};
if( value is PropertyString ) {
TextBox tb = new TextBox();
tb.SetBinding( TextBox.TextProperty, ValueBinding );
tb.SetBinding( TextBox.IsEnabledProperty, EditableBinding );
return tb;
} else if( value is PropertyBoolean ) {
CheckBox cb = new CheckBox();
cb.SetBinding( CheckBox.IsCheckedProperty, ValueBinding );
cb.SetBinding( CheckBox.IsEnabledProperty, EditableBinding );
return cb;
} ....
return null;
}
public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
throw new NotImplementedException();
}
}
And the xaml code:
...
xmlns:Converters="clr-namespace:MyConverters"
...
<UserControl.Resources>
<Converters:PropertyConverter x:Key="PropertyInput"/>
</UserControl.Resources>
...
<ListBox ItemsSource="{Binding Path=ItemProperties.GeneralProperties}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="320" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Grid.Column="0" />
<ContentPresenter Content="{Binding Converter={StaticResource PropertyInput}}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Two artikel:
http://shawnoster.com/blog/post/Dynamic-Icons-in-the-Silverlight-TreeView.aspx
http://www.silverlightshow.net/items/Silverlight-2-Getting-to-the-ListBoxItems-in-a-ListBox.aspx

Windows Phone Silverlight - setting button IsDisabled if bound data length is 0

Here's what I've got - I'm writing an App that, among other things, reads an RSS feed to get episodes of a certain podcast, then displays each episode's title and description, with a "listen" and "watch" button. But not all the episodes have both options - the RSS will return an empty string instead of a URL for either option if it's not available. So I'm trying to use IValueConverter that I can bind IsDisabled to, which returns true if the bound data length is 0, and false otherwise. For now, I'm just testing it on the "watch" buttons, since the binding will be nearly identical for the "listen" buttons.
A snippet of MainPage.xaml.cs:
using System.Xml.Linq;
using System.Windows.Data;
namespace appname
{
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
WebClient PodcastListDownloader = new WebClient();
PodcastListDownloader.DownloadStringCompleted += new DownloadStringCompletedEventHandler(PodcastListDownloadCompleted);
PodcastListDownloader.DownloadStringAsync(new Uri("http://domain.tld/mobile_app/podcastfeed"));
}
void PodcastListDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
return;
XElement xmlPodcastList = XElement.Parse(e.Result);
PodcastListBox.ItemsSource = from PodcastEpisode in xmlPodcastList.Descendants("item")
select new PodcastItem
{
title = PodcastEpisode.Element("date").Value + " " + PodcastEpisode.Element("title").Value,
subtitle = PodcastEpisode.Element("subtitle").Value,
description = PodcastEpisode.Element("summary").Value,
audio = PodcastEpisode.Element("audio").Value,
video = PodcastEpisode.Element("video").Value,
};
}
private void PlayPodcast(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
Microsoft.Phone.Tasks.MediaPlayerLauncher PodcastPlay = new Microsoft.Phone.Tasks.MediaPlayerLauncher();
PodcastPlay.Media = new Uri(btn.Tag.ToString());
PodcastPlay.Show();
}
}
public class PodcastItem
{
public string title { get; set; }
public string description { get; set; }
public string audio { get; set; }
public string video { get; set; }
public string subtitle { get; set; }
}
public class StringLengthVisibilityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || value.ToString().Length == 0)
{
return false;
}
else
{
return true;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
A snippet of MainPage.xaml:
<phone:PhoneApplicationPage
x:Class="CCoFnow.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="False">
<Grid x:Name="LayoutRoot" Background="Transparent">
<!--Panorama control-->
<controls:Panorama Title="AppName">
<controls:Panorama.Background>
<ImageBrush ImageSource="PanoramaBackground.png"/>
</controls:Panorama.Background>
<controls:PanoramaItem Header="Podcast" Foreground="{StaticResource PhoneAccentBrush}">
<ListBox Margin="0,0,-12,0" ItemsSource="{Binding Items}" Name="PodcastListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding title}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding description}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
<StackPanel Orientation="Horizontal">
<Button Content="Listen" Width="215" Tag="{Binding audio}" Click="PlayPodcast"/>
<Button Content="Watch" Width="215" Tag="{Binding video}" Click="PlayPodcast" IsEnabled="{Binding video, Converter={StringLengthVisibilityConverter}}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PanoramaItem>
</controls:Panorama>
</Grid>
</phone:PhoneApplicationPage>
But the debugger is throwing two errors:
1) The tag
'StringLengthVisibilityConverter' does
not exist in XML namespace
'http://schemas.microsoft.com/winfx/2006/xaml/presentation
2) The type
'StringLengthVisibilityConverter' was
not found. Verify that you are not
missing an assembly and that all
referenced assemblies have been built
I set the converter to {StaticResource StringLengthVisibilityConverter} instead (per http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter(v=VS.95).aspx), and now there's just one error: The resource "StringLengthVisibilityConverter" could not be resolved. With this error, I can debug (run) the code, but all the "watch" buttons remain enabled.
So I'm guessing I'm calling it in the wrong namespace, but I can't seem to figure out the correct one. Can someone please point me in the right direction?
Thanks!
Edit: In the process of putting this together, I realized that I need to do this differently - the feed now has additional values I can databind to. However, I'm quite sure I'm going to need this functionality at some point in the future, so I'm going to post anyway. If there's an easy solution for the question, please let me know so I can learn and do it sucessfully next time!
The way you reference the converter isn't quite right. You need an instance of the converter available somwhere, e.g. in the page's Resources section:
<phone:PhoneApplicationPage xmlns:conv="namespace reference for your converter goes here"
...>
<phone:PhoneApplicationPage.Resources>
<conv:StringLengthVisibilityConverter x:Key="Length" />
</phone:PhoneApplicationPage.Resources>
Then you reference that converter by using a StaticResource reference with the x:Key that you gave the converter.
<Button Content="Watch"
Width="215"
Tag="{Binding video}"
Click="PlayPodcast"
IsEnabled="{Binding video, Converter={StaticResource Length}}"/>
I'll leave the discussion of your approach versus using commands and MVVM for another day :)

Resources