I try to bind string (which is a path to png) in ViewModel to grid background. This string can be null. It works good but in output cmd is
System.Windows.Data Error: 23 : Cannot convert '' from type '' to type 'System.Windows.Media.ImageSource' for 'en-US' culture with default conversions; consider using Converter
How I can bind a string path to the background to avoid this error?
My current binding:
FieldControl.CS:
public string BackgroundPath
{
get
{
return (string)GetValue(BackgroundPathProperty);
}
set
{
SetValue(BackgroundPathProperty, value);
}
}
public static readonly DependencyProperty BackgroundPathProperty =
DependencyProperty.Register("BackgroundPath", typeof(string), typeof(FieldControl), new PropertyMetadata(null));
FieldControl.XAML:
<Grid.Background>
<ImageBrush
ImageSource="{
Binding Path=BackgroundPath,
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged,
RelativeSource={
RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}
}
}" />
</Grid.Background>
And in MainWindow.XAML:
<ItemsControl
ItemsSource="{Binding FieldsVM}">
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:FieldControl
BackgroundPath="{Binding Path=BackgroundPath }"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
One way to remove the error from the compiler, is to create an IValueConverter and using in the ImageBrush element, as the compiler suggest.
The IValueConverter could be writing like this:
[ValueConversion(typeof(string), typeof(ImageSource))]
public class StringToImageSourceConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value == null) return null;
var path = value.ToString();
return new BitmapImage(new Uri(path, UriKind.RelativeOrAbsolute));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotSupportedException();
}
}
and in the FieldControl's xaml:
<UserControl.Resources>
<local:StringToImageSourceConverter x:Key="StringToImageSourceConverter" />
</UserControl.Resources>
<Grid Height="100">
<Grid.Background>
<ImageBrush ImageSource="{Binding Path=BackgroundPath, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, ElementName=Uc1,
Converter={StaticResource StringToImageSourceConverter}}" />
</Grid.Background>
</Grid>
After this, you shouldn't see the error from the compiler.
I test the code in this project.
Related
Creating button which reacts to a dynamic resource (style for dark and light themes) is done like this:
<Button>
<Image Source="{DynamicResource IconId_12}" />
</Button>
The difficulty comes about when attempting the same concept for an ItemsControl of buttons with different icons for each button, each which have a key which refers to either a dark or light themed image source:
<ItemsControl ItemsSource="{Binding ButtonVMs}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type ButtonVM}">
<Button Command="{Binding ClickCommand}">
<Image Source="{DynamicResource {Binding IconKey}}" />
</Button>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Where ButtonVM looks like this:
public class ButtonVM {
public Command ClickCommand { get; set; }
public string IconKey { get; set; }
}
How can I accomplish binding the resource key name into the dynamic binding?
I have noted that in code you can use <FrameworkElement>.SetResourceReference(SourceProperty, "IconKey"). (as suggested in this stackoverflow answer). But the problem here is that the VM is not a FrameworkElement.
Using a multi-value converter, I was able to access the FrameworkElement (Image) and utilize .SetResourceReferece() to achieve the effect I needed.
public class ImageSourceKeyToDynamicResourceConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var image = (Image)values[0];
var key = (string)values[1];
image.SetResourceReference(Image.SourceProperty, key);
return frameworkElement.GetValue(Image.SourceProperty);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The xaml sideof things:
<Image>
<Image.Source>
<MultiBinding Converter="{StaticResource KeyToDynamicResourceConverter}">
<Binding RelativeSource="{RelativeSource Self}" />
<Binding Path="IconKey" Mode="OneWay" />
</MultiBinding>
</Image.Source>
</Image>
I have a ListView in XAML and a List<string> that holds local embedded image paths. I am not able to show images in List. By the way I am able to show as a single image by
<Image Source="{local:ImageResource TypingApplication.Images.Icons.Search.png}" />
But I cannot show the images in ListView. Here is my XAML code
<ListView x:Name="ListView"
ItemsSource="{Binding ListItems}"
IsEnabled="True"
IsVisible="True"
RowHeight="40"
Opacity="0.9">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Image Source="{local:ImageResource TypingApplication.Images.Icons.{Binding .}}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have added ImageResourceExtension in Extensions folder and xmlns:local="clr-namespace:TypingApplication.Extensions" in XAML, as I mentioned I can show Single Image, only there is problem with ListView.
Here is my C# code that contains List and Constructor
public List<string> ListItems
{
get
{
return new List<string>()
{
"Home.png",
"Favorite.png",
"Search.png"
};
}
}
public HomePage()
{
InitializeComponent();
this.BindingContext = this;
}
Please note that I am using shared Images in my project. I have set Properties of all Images to Embedded resource in SolutionExplorer.
Change list to ObservableCollection
IValueConverter implementation to convert your binding to desired value
The image property should be set to EmbeddedResource
public class EmbeddedToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string fileName && parameter is String assemblyName)
{
try
{
var imageSource = ImageSource.FromResource(assemblyName + "." + fileName, typeof(EmbeddedToImageSourceConverter).GetTypeInfo().Assembly);
return imageSource;
}
catch (Exception)
{
return value;
}
}
else
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
XAML
<ContentPage.Resources>
<local:EmbeddedToImageSourceConverter x:Key="converter" />
</ContentPage.Resources>
In the listview add binding w.r.to converter resource we just created.
<Image Source="{Binding ., Converter={StaticResource converter}, ConverterParameter='TypingApplication.Images.Icons'}" />
If you are not using View Model (MVVM), you can directly specify image file's name in XAML as:
<Image Source="{Binding Source='ImageFileName.png', Converter={StaticResource converter}, ConverterParameter='TypingApplication.Images.Icons'}" />
If you want to add Embedded image in listview, according to json's reply, your binding have some problem, you can use IValueConverter to convert image path as correct.
I do one sample according to your code, you can take a look:
<ListView HasUnevenRows="True" ItemsSource="{Binding ListItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Image
HeightRequest="100"
Source="{Binding ., Converter={StaticResource imageconverter}}"
WidthRequest="100" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ContentPage.Resources>
<local:imageconverter x:Key="imageconverter" />
</ContentPage.Resources>
The Image converter:
public class imageconverter : IValueConverter
{
public string Source { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Source = (string)value;
if (Source == null)
return null;
// Do your translation lookup here, using whatever method you require
var imageSource = ImageSource.FromResource("demo3."+Source, typeof(ImageResourceExtension).GetTypeInfo().Assembly);
return imageSource;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can change demo3 as TypingApplication according to your code.
public partial class Page14 : ContentPage
{
public ObservableCollection<string> ListItems { get; set; }
public Page14()
{
InitializeComponent();
ListItems = new ObservableCollection<string>()
{
"image1.jpg","image2.png","image3.png"
};
this.BindingContext = this;
}
}
As Prateek's reply, I suggest you can change List<> to Observablecollection<>, because it implement INotifyPropertyChanged interface, notify data changed.
https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=netframework-4.8
I have a traffic application. The light status is updated in the listbox.
<ListBox x:Name="lbxCallProgress" ItemsSource="{Binding Messages,Mode=TwoWay}" Height="373" FontSize="8" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
For the Messages:
public partial class MainWindow : Window
{
public ObservableCollection<string> Messages { get; set; }
To update messages to the listbox.
void UpdateMessage(string message)
{
try
{
Dispatcher.BeginInvoke((Action)delegate()
{
Dispatcher.BeginInvoke(new Action(() => { this.Messages.Add(message); }));
});
}
Now if the string message contains the keyword "green", then I want to set the item color on the listbox as color green, etc.
How?
here you go
with the power of WPF binding you can use the value to bind to the desired property Background and the implicit converter will do the rest for you.
<DataTemplate>
<TextBlock Text="{Binding}" Background="{Binding}"/>
</DataTemplate>
you can choose to bind Foreground in case if you want to change the text color
Using converters
if simple binding is not sufficient enough you may use converters to perform custom logic of conversion, eg converting The light is red to Brushes.Red
public class MyColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = value as string;
if(text.Contains("red"))
return Brushes.Red;
return Brushes.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
usage
<DataTemplate xmlns:l="your namespace to converter class">
<DataTemplate.Resources>
<l:MyColorConverter x:Key="MyColorConverter" />
</DataTemplate.Resources>
<TextBlock Text="{Binding}" Background="{Binding Converter={StaticResource MyColorConverter}}"/>
</DataTemplate>
I want to convert the localization meta tag e.g. en-US to the display name, in this case English. The meta tag is stored in a ObservableCollection because it will be modified on runtime. I want to bind the display name to a combo box.
ComboBox:
<ComboBox Grid.Column="1" Grid.Row="1" Width="200" VerticalAlignment="Center" HorizontalAlignment="Center" SelectedIndex="0" ItemsSource="{Binding Path=ServerData.AvailableTemplateLanguages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding, Converter=LanguageTagToNameConverter}" FontSize="12"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Converter:
class LanguageTagToNameConverter : IValueConverter
{
public object Convert(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
return CultureInfo.GetCultureInfo(value.ToString()).DisplayName;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
ObservableCollection:
public class ServerDataObj : ModelBase
{
private ObservableCollection<string> _availableTemplateLanguages = new ObservableCollection<string> { "de-DE", "en-US" };
public ObservableCollection<string> AvailableTemplateLanguages
{
get
{
return _availableTemplateLanguages;
}
set
{
_availableTemplateLanguages = value;
OnPropertyChanged("AvailableTemplateLanguages");
}
}
}
Unfortunately this approach does not work.
You need to put converter into resources:
<Window>
<Window.Resources>
<LanguageTagToNameConverter x:Key="convLang"/>
</Window.Resources>
...
<TextBlock Text="{Binding, Converter={StaticResource convLang}}"/>
I need to make some complex binding in XAML. I have a DependencyProperty typeof(double); let's name it SomeProperty. Somewhere in XAML code of my control, I need to use the whole SomeProperty value, somewhere only a half, somewhere SomeProperty/3, and so on.
How can I do something like:
<SomeControl Value="{Binding ElementName=MyControl, Path=SomeProperty} / 3"/>
:)
Looking forward.
Use a division ValueConverter:
public class DivisionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int divideBy = int.Parse(parameter as string);
double input = (double)value;
return input / divideBy;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
<!-- Created as resource -->
<local:DivisionConverter x:Key="DivisionConverter"/>
<!-- Usage Example -->
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=1}"/>
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=2}"/>
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=3}"/>