I want to change the image displayed in a grid depending on a certain condition (e.g. an enum value). The problem is that the image sources are DrawingImage (provided from xaml file).
<Grid>
<Image Source="{StaticResource BodyDrawingImage}"></Image>
</Grid>
I want that BodyDrawingImage (which is a key for DrawingImage) to be selected at run-time (by a binding i guess) from a list of DrawingImage keys but I can't figure out how to do this.
The way I've handled this is using a value converter class, with a property corresponding to each element of the Enum type.
public class perDialogIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is perDialogIcon))
{
return null;
}
switch ((perDialogIcon) value)
{
case perDialogIcon.Asterisk:
return AsteriskIcon;
case perDialogIcon.Error:
return ErrorIcon;
case perDialogIcon.Exclamation:
return ExclamationIcon;
case perDialogIcon.Hand:
return HandIcon;
case perDialogIcon.Information:
return InformationIcon;
case perDialogIcon.Question:
return QuestionIcon;
case perDialogIcon.Stop:
return StopIcon;
case perDialogIcon.Warning:
return WarningIcon;
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public UIElement AsteriskIcon { get; set; }
public UIElement ErrorIcon { get; set; }
public UIElement ExclamationIcon { get; set; }
public UIElement HandIcon { get; set; }
public UIElement InformationIcon { get; set; }
public UIElement QuestionIcon { get; set; }
public UIElement StopIcon { get; set; }
public UIElement WarningIcon { get; set; }
}
This is instantuated as a resource within the window, along with each image required
<Window.Resources>
<Image x:Key="AsteriskIcon"
Source="Images/Asterisk.png"
Stretch="None" />
<Image x:Key="ErrorIcon"
Source="Images/Error.png"
Stretch="None" />
<Image x:Key="ExclamationIcon"
Source="Images/Exclamation.png"
Stretch="None" />
<Image x:Key="HandIcon"
Source="Images/Hand.png"
Stretch="None" />
<Image x:Key="InformationIcon"
Source="Images/Information.png"
Stretch="None" />
<Image x:Key="QuestionIcon"
Source="Images/Question.png"
Stretch="None" />
<Image x:Key="StopIcon"
Source="Images/Stop.png"
Stretch="None" />
<Image x:Key="WarningIcon"
Source="Images/Warning.png"
Stretch="None" />
<dlg:perDialogIconConverter x:Key="DialogIconConverter"
AsteriskIcon="{StaticResource AsteriskIcon}"
ErrorIcon="{StaticResource ErrorIcon}"
ExclamationIcon="{StaticResource ExclamationIcon}"
HandIcon="{StaticResource HandIcon}"
InformationIcon="{StaticResource InformationIcon}"
QuestionIcon="{StaticResource QuestionIcon}"
StopIcon="{StaticResource StopIcon}"
WarningIcon="{StaticResource WarningIcon}" />
</Window.Resources>
and then used as required as part of the binding statement - DialogIcon is an enum property on the ViewModel
<ContentPresenter Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Center"
Content="{Binding DialogIcon, Converter={StaticResource DialogIconConverter}}" />
Related
I would really like to know how I can bind an ImageSource to a specific element's property in an ObservableCollection... Right now I have this:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyNameSpace"
x:Class="MyNameSpace.MainPage"
x:Name = "Main"
Padding="5,20,5,5"
BindingContext="{x:Reference Name=Main }">
<Grid
<ImageButton
Grid.Column="0"
Grid.Row="0"
Source="{Binding _hand[0].ResourceId , Converter={StaticResource
StringToSourceConverter}}"
>
</ImageButton>
<ImageButton
Grid.Column="1"
Grid.Row="0"
Source="{Binding _hand[1].ResourceId , Converter={StaticResource
StringToSourceConverter}}"
>
</ImageButton>
<ImageButton
Grid.Column="0"
Grid.Row="1"
Source="{Binding _hand[2].ResourceId , Converter={StaticResource
StringToSourceConverter}}"
>
</ImageButton>
<ImageButton
Grid.Column="1"
Grid.Row="1"
Source="{Binding _hand[3].ResourceId , Converter={StaticResource
StringToSourceConverter}}"
>
</ImageButton>
</Grid>
</ContentPage>
I would like to bind the ImageSource to the following ObservableCollection of Cards...
public partial class MainPage : ContentPage, INotifyPropertyChanged
{
private ObservableCollection<Card> _hand;
public MainPage()
{
Init();
InitializeComponent();
}
private void Init()
{
_hand = new ObservableCollection<Card>()
{
new Card("image1.jpg"),
new Card("image2.jpg"),
new Card("image3.jpg"),
new Card("image4.jpg")
};
}
}
My Card class looks something like this:
public Card ( string resourceId)
{
ResourceId = resourceId;
}
public string ResourceId { get; set; }
The Converter used :
public class ToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string ResourceId = value.ToString();
if (String.IsNullOrWhiteSpace(ResourceId))
return null;
return ImageSource.FromResource(ResourceId);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
My question now is how do I make this code work? Also I have methods that switch elements in the collection. Where do I implement PropertyChangedEvent? Thank you guys a lot :)
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'm trying to bind the Visibility property of a FontIcon to an enum property in my view model using a converter, but for some reason it throws an exception
Unable to cast object of type 'Windows.UI.Xaml.Controls.FontIcon' to type
'Windows.UI.Xaml.Data.Binding'
What I want to achieve is that depending on the current value of CurrentSortOrder hide or show an icon inside the MenuFlyoutItem
View model code:
public class TestViewModel : ViewModelBase
{
private TaskSortType _currentTaskSortOrder = TaskSortType.BY_NAME_ASC;
public TaskSortType CurrentSortOrder
{
get => _currentTaskSortOrder;
set => Set(ref _currentTaskSortOrder, value);
}
}
View:
<Page
x:Class="UWPTests.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:UWPTests.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:UWPTests"
xmlns:localModels="using:UWPTests.Models"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{x:Bind ViewModel}"
mc:Ignorable="d">
<Page.Resources>
<converters:TaskSortTypeToVisibilityConverter x:Key="TaskSortTypeToVisibilityConverter" />
</Page.Resources>
<Grid>
<AppBarButton Icon="Sort" Label="Sort">
<AppBarButton.Flyout>
<MenuFlyout>
<MenuFlyoutSubItem Text="By name">
<MenuFlyoutItem Text="Asc">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="" Visibility="{Binding CurrentSortOrder, Mode=OneWay, Converter={StaticResource TaskSortTypeToVisibilityConverter}, ConverterParameter={x:Bind localModels:TaskSortType.BY_NAME_ASC}}" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Desc">
<MenuFlyoutItem.Icon>
<FontIcon Glyph="" Visibility="Collapsed" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
</MenuFlyoutSubItem>
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
</Grid>
Converter:
public class TaskSortTypeToVisibilityConverter : IValueConverter
{
public Visibility OnTrue { get; set; }
public Visibility OnFalse { get; set; }
public TaskSortTypeToVisibilityConverter()
{
OnFalse = Visibility.Collapsed;
OnTrue = Visibility.Visible;
}
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is null || parameter is null)
return Visibility.Collapsed;
var currentOrder = (TaskSortType)value;
var targetOrder = (TaskSortType)parameter;
return currentOrder == targetOrder ? OnTrue : OnFalse;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is Visibility == false)
return DependencyProperty.UnsetValue;
if ((Visibility)value == OnTrue)
return true;
else
return false;
}
}
Any help would be appreciated
Edit:
I get the exception here: this.InitializeComponent();
public sealed partial class MainPage : Page
{
public TestViewModel ViewModel { get; set; }
public MainPage()
{
ViewModel = new TestViewModel();
this.InitializeComponent();
}
}
Edit 2:
public enum TaskSortType
{
BY_NAME_ASC = 0,
BY_NAME_DESC = 1,
BY_UPDATED_DATE_ASC = 2,
BY_UPDATED_DATE_DESC = 3,
}
It seems that i cant use x:Bind directly in the ConverterParameter .. So i ended with the following:
I added in my page resources:
<localModels:TaskSortType x:Key="TaskSortByNameAsc">BY_NAME_ASC</localModels:TaskSortType>
<localModels:TaskSortType x:Key="TaskSortByNameDesc">BY_NAME_DESC</localModels:TaskSortType>
<localModels:TaskSortType x:Key="TaskSortByUpdatedDateAsc">BY_UPDATED_DATE_ASC</localModels:TaskSortType>
<localModels:TaskSortType x:Key="TaskSortByUpdatedDateDesc">BY_UPDATED_DATE_DESC</localModels:TaskSortType>
And then i replaced the ConverterParameter binding with the following:
<FontIcon Glyph="" Visibility="{Binding CurrentSortOrder, Mode=OneWay, Converter={StaticResource TaskSortTypeToVisibilityConverter}, ConverterParameter={StaticResource BY_NAME_ASC}}" />
Another workaround would be to pass the corresponding value in the ConverterParameter, for example ConverterParameter=0 or ConverterParameter="BY_NAME_ASC"and the cast that parameter to the corresponding enum value
I need to set the background value depending on a value that you receive in the constructor.
The types are:
public enum EToastType
{
Error,
Info,
Success,
Warning
}
CustomMessage.xaml:
<core:NotificationDisplayPart x:Class="Presentacion.Notificaciones.CustomMessage"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:core="clr-namespace:ToastNotifications.Core;assembly=ToastNotifications"
mc:Ignorable="d" Background="{**I NEED SET VALUE**}"
d:DesignHeight="60" d:DesignWidth="250">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Title}" FontWeight="Bold" Foreground="White" />
<TextBlock Text="{Binding Message}" FontWeight="Light" Foreground="White" Grid.Row="1" TextWrapping="Wrap" />
</Grid>
CustomMesage.xaml.cs:
public partial class CustomMessage : NotificationDisplayPart
{
public CustomMessage(ToastDto toast)
{
DataContext = toast;
InitializeComponent();
}
}
ToastDto.cs:
public class ToastDto
{
public EToastType Color { get; set; }
public string Title { get; set; }
public string Message { get; set; }
}
And App.xaml:
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:options="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options">
<Application.Resources>
<ResourceDictionary>
<Color x:Key="InformationColor">#147ec9</Color>
<SolidColorBrush x:Key="InformationColorBrush" Color="{StaticResource InformationColor}" options:Freeze="True" />
<Color x:Key="SuccessColor">#11ad45</Color>
<SolidColorBrush x:Key="SuccessColorBrush" Color="{StaticResource SuccessColor}" options:Freeze="True" />
<Color x:Key="ErrorColor">#e60914</Color>
<SolidColorBrush x:Key="ErrorColorBrush" Color="{StaticResource ErrorColor}" options:Freeze="True" />
<Color x:Key="WarningColor">#f5a300</Color>
<SolidColorBrush x:Key="WarningColorBrush" Color="{StaticResource WarningColor}" options:Freeze="True" />
</ResourceDictionary>
</Application.Resources>
Then depending on the EToastType value that is sent to the CustomMessage constructor, the background property in CustomMessage has to take a value of App.xaml
You can write a custom IValueConverter to convert your EToastType to Brush.
public class EToastTypeToBrushConverter : IValueConverter
{
public Brush Error { get; set; }
public Brush Info { get; set; }
public Brush Success { get; set; }
public Brush Warning { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is EToastType type)
{
switch (type)
{
case EToastType.Error:
return Error;
case EToastType.Info:
return Info;
case EToastType.Success:
return Success;
case EToastType.Warning:
return Warning;
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
By using this converter, just initialize a new instance with each brush property in your resource dictionary.
<local:EToastTypeToBrushConverter x:Key="EToastTypeToBrushConverter"
Info="{StaticResource InformationColorBrush}"
Success="{StaticResource SuccessColorBrush}"
Error="{StaticResource ErrorColorBrush}"
Warning="{StaticResource WarningColorBrush}"/>
EDIT: If you want a more universal IValueConverter, you can write such code instead of EToastTypeToBrushConverter:
[ContentProperty(nameof(Conversions))]
public class EnumToObjectConverter : IValueConverter
{
public Collection<EnumConversion> Conversions { get; } = new Collection<EnumConversion>();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> Conversions.FirstOrDefault(x => Equals(x.Source, value))?.Target;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
But in this way, XAML usage will be more complex:
<local:EnumToObjectConverter x:Key="EToastTypeToBrushConverter">
<local:EnumConversion Target="{StaticResource InformationColorBrush}">
<local:EnumConversion.Source>
<local:EToastType>Info</local:EToastType>
</local:EnumConversion.Source>
</local:EnumConversion>
<local:EnumConversion Target="{StaticResource SuccessColorBrush}">
<local:EnumConversion.Source>
<local:EToastType>Success</local:EToastType>
</local:EnumConversion.Source>
</local:EnumConversion>
<local:EnumConversion Target="{StaticResource ErrorColorBrush}">
<local:EnumConversion.Source>
<local:EToastType>Error</local:EToastType>
</local:EnumConversion.Source>
</local:EnumConversion>
<local:EnumConversion Target="{StaticResource WarningColorBrush}">
<local:EnumConversion.Source>
<local:EToastType>Warning</local:EToastType>
</local:EnumConversion.Source>
</local:EnumConversion>
</local:EnumToObjectConverter>
I want to make label that have FontWeight attribute dependent on checkbox.
If checkbox is checked then label font weight is bold, if not - then font weight is normal. I decided to bind FontWeight attribute in Label with Checkbox and add converter.
Here is screen what I want to achieve (text on bottom should be bolded when Bold is checked)
enter image description here
I created all the logic and binding but when I click on Bold checkbox nothing happends.
Here is what I have so far
MainWindow.xaml
<Window ...
...
<Window.Resources>
<local:BoolToStringConverter x:Key="custom" TrueValue="Bold" FalseValue="Normal" />
</Window.Resources>
<Grid>
...
<CheckBox Name="BoldField" Margin="5" FontWeight="Bold">Bold</CheckBox>
<Label Name="text" Grid.Row="3" Grid.ColumnSpan="3"
FontWeight="{Binding BoldField, Converter={StaticResource custom}}" />
...
</Window>
MainWindow.xaml.cs
namespace Wpf03
{
public class BoolToValueConverter<T> : IValueConverter
{
public T FalseValue { get; set; }
public T TrueValue { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return FalseValue;
else
return (bool)value ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value != null ? value.Equals(TrueValue) : false;
}
}
public class BoolToStringConverter : BoolToValueConverter<FontWeight> { }
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
I tried with different converters but no result. Why my converter is never called? Any ideas?
You are passing the Name of the CheckBox as the Binding Path
instead you need to specify the IsChecked property:
<CheckBox Name="BoldField" Margin="5" FontWeight="Bold">Bold</CheckBox>
<Label Name="text" Grid.Row="3" Grid.ColumnSpan="3"
FontWeight="{Binding ElementName=BoldField, Path=IsChecked, Converter={StaticResource custom}}" />