I have a wpf application ,in which I have a tabcontrol ,I set its ItemTemplate from an external resource dictionary,in that template I have an Image Control which I bind to a string Property 'ImagePath' in my ViewModel and using a converter I convert 'ImagePath' into new bitmapImage .I created my converter class instance in the external resource disctinary. I merged the external dictionary in my App.xaml
Here is my External Resource Dictionary:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:helpers="clr-namespace:RH_Maize.Helper">
<helpers:ImageSourceConverter x:Key="ImageSourceConverter" />
<DataTemplate x:Key="ClosableTabItemTemplate">
<StackPanel Width="155"
Orientation="Horizontal"
Height="35">
<Image Height="25"
Width="30"
Source="{Binding Path=ImagePath,Converter={ StaticResource ImageSourceConverter}}"/>
<TextBlock TextWrapping="Wrap" Text="{Binding DisplayName}"
Width="100" Height="27" VerticalAlignment="Bottom" FontSize="15" Margin="3,0,0,0"/>
<Button Command="{Binding CloseCommand}"
Content="x" />
</StackPanel>
</DataTemplate>
Here is my Converter class :
public class ImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType == typeof(ImageSource))
{
if (value is string)
{
string str = (string)value;
return new BitmapImage(new Uri(str, UriKind.RelativeOrAbsolute));
}
else if (value is Uri)
{
Uri uri = (Uri)value;
return new BitmapImage(uri);
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
My ViewModel Property is :
private string _ImagePath;
public string ImagePath
{
get
{
return _ImagePath;
}
set
{
_ImagePath = value;
RaisePropertyChanged(() => ImagePath);
}
}
My problem is that the converter classes convert method is not invoking at all .Whats wrong with my code ?
Related
I had bind the error method property with text-block so that when error occurs it is shown down the textbox in the form. But i also want to change the background color of the textbox when the error occurs. But i don't know how to bind the border-brush property dynamically with text-box when the error occur. I have shared the small portions of my code.
XAML code
<TextBlock
Margin="0 110 0 12"
Style="{StaticResource Label}"
Text="Name"
/>
<TextBox
Margin="0 0 300 0" MaxLength="20" Text="{x:Bind ViewModel.Name, Mode=TwoWay}"
/>
<TextBlock Style="{StaticResource ErrorStyle}" Text="{x:Bind ViewModel.IsNameValid, Mode=TwoWay}"/>
ViewModel
private string IsNameValid_;
public string IsNameValid
{
get => this.IsNameValid_;
set => SetProperty(ref this.IsNameValid_, value);
}
public async Task RegisterPatientInfo()
{
ValidationCheck(this.Name,);
}
ValidationMethod
public void ValidationCheck(string name, string kana, string patientNumber, string year, string month, string days)
{
//Name Validation
var regexNamePattern = #"^([a-zA-Z]){0,20}$";
Regex nameRegex = new Regex(regexNamePattern);
if (!nameRegex.IsMatch(name))
{
this.IsNameValid = "";
}
A common way is to use data-binding with ValueConverter to dynamically change the color of an UIelement.
Here is a simple demo that you could refer to.
ColorConverter:
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
SolidColorBrush brush = null;
string IsNameValid = value as string;
if (IsNameValid.Equals("123"))
{
brush = new SolidColorBrush(Colors.Red);
}
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Xaml:
<Page.Resources>
<local:ColorConverter x:Key="ColorConverter"/>
</Page.Resources>
<TextBox Background="{x:Bind ViewModel.IsNameValid,Converter={StaticResource ColorConverter}, Mode=TwoWay}"/>
In my code I have a class with the following fields:
public class Source
{
public string Name { get; set; }
...
public string Path { get => $"/Assets/Images/{Name}.svg"; }
}
Path property is there just for debugging.
I also develop a converter in order to get rid of Path property.
public class SourceToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return $"/Assets/Images/{value}.svg";
}
}
When I use Path property as Image Source, everything works fine, but when I try to do the same with the SourceToImageConverter, app is not working.
I know converter is working as it Should cause when I use it on a TextBlock it shows the right value.
Xaml code looks like:
<Page.Resources>
<local:SourceToImageConverter x:Key="SourceToImage"/>
<DataTemplate x:Key="SourceListViewTemplate" x:DataType="models:Source">
<StackPanel Orientation="Horizontal">
<Image Source="{x:Bind Path}"/>
<Image Source="{x:Bind Name, Converter={StaticResource SourceToImage}}"/>
<TextBlock Text="{x:Bind Name, Converter={StaticResource SourceToImage}}"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
...
<GridView
x:Name="Source"
ItemsSource="{x:Bind ViewModel.Sources}"
ItemTemplate="{StaticResource SourceListViewTemplate}"
IsItemClickEnabled="True"
SelectionMode="Single"/>
Apply XamlBindingHelper.ConvertValue() to the value just as auto-generated {x:Bind} code is doing backstage.
public class SourceToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
//if (targetType == typeof(string)) return $"/Assets/Images/{value}.svg";
return XamlBindingHelper.ConvertValue(typeof(ImageSource), $"/Assets/Images/{value}.svg");
}
}
I created a ComboBox listing the colors that System.Windows.Media.Colors predefines, using the approach told in this question: How can I list colors in WPF with XAML?
My XAML code now is:
<Window ...>
<Window.Resources>
<ObjectDataProvider
ObjectInstance="{x:Type Colors}" MethodName="GetProperties" x:Key="ColorList" />
<local:StringToBrushConverter x:Key="FontColorConversions" />
</Window.Resources>
<Grid Background="Black">
...
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource ColorList}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="{Binding Converter={StaticResource FontColorConversions}}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
...
</Grid>
</Window>
And besides, please note that I bind SelectedValue to a VM class's FontColor property, which is of string type.
class FontSetting : INotifyPropertyChanged
{
private string _fontColor = "Lavender"; // initial color
public event PropertyChangedEventHandler PropertyChanged;
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
OnPropertyChanged("FontColor");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And I set the DataContext of the Window containing this ComboBox to an instance of FontSetting.
So each item in the ComboBox actually display a string representing a certain color now, what I want to do is set an item's Foreground color to that color its content indicates, like this:
Can anyone help? Thanks.
UPDATED:
Since most of the solutions have a converter which converts string to Brush and actually I already have it, now I want to put mine here, as I binded a TextBox's Foreground to FontSetting's FontColor property, so that when you change the ComboBox, the color of that TextBox changes accordingly.
Here is my converter class, and it works fine by now:
class StringToBrushConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BrushConverter conv = new BrushConverter();
SolidColorBrush brush = conv.ConvertFromString("Lavender") as SolidColorBrush;
if (null != value)
{
brush = conv.ConvertFromString(value.ToString()) as SolidColorBrush;
}
return brush;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
When I click the ComboBox to open the dropdown list, I got an exception:
CONCLUSION
Amine's solution works, it's my mistake. I explain briefly now, if you bind a ComboBox to System.Windows.Media.Colors like what I am doing, when the item is rendered, the Convert() method of the converter class (which you assign to the binding) is executed, and actually the value passed to Convert() as its first parameter is a Syetem.Windows.Media.Color instance. I made mistake coz I thought it was of string type.
Therefore, in my case I need two converter classes, one converting string to Brush, and the other one converting Color to Brush. So I will keep my own StringToBrush converter and add Amine's ColorToBrush converter.
However, I simplified Amine's implementation a bit:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BrushConverter conv = new BrushConverter();
SolidColorBrush brush = SolidColorBrush)conv.ConvertFromString(FontSetting.DEFAULT_FONT_COLOR);
if (null != value)
{
PropertyInfo pi = value as PropertyInfo;
if (null != pi)
{
brush = conv.ConvertFromString(pi.Name) as SolidColorBrush;
}
}
return brush;
}
Moreover, Joe's input is also valuable, put all them together, I can keep the items' color consistent, which is perfect.
You can set Style of ComboBoxItem as bellow :
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240" x:Name="CB"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
DisplayMemberPath="Name"
SelectedValuePath="Name">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="{Binding Converter={StaticResource converter}}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
By using this converter:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
object obj = ((System.Reflection.PropertyInfo)value).GetValue(this,null);
return (SolidColorBrush)new BrushConverter().ConvertFromString(obj.ToString());
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return value;
}
}
Two ways, use a value converter or an intermediate property. The easiest is probably an intermediate property as you are using well structured bindings already for your SelectedItem (but value converters are fun too!).
SelectedValue is bound to FontColor, so in your setter set another value:
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
ForegroundColorToDisplay = GetBrushFromColorString(value);
OnPropertyChanged("FontColor");
}
}
public Brush _foregroundColorToDisplay
public Brush ForegroundColorToDisplay
{
get
{
return _foregroundColorToDisplay;
}
set
{
_foregroundColorToDisplay= value;
OnPropertyChanged("ForegroundColorToDisplay");
}
}
or, if you don't want to store it:
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
//note it fires two changed events!
OnPropertyChanged("ForegroundColorToDisplay");
OnPropertyChanged("FontColor");
}
}
public Brush ForegroundColorToDisplay
{
get
{
return GetBrushFromColorString(value);;
}
}
You can bind to this new property in your xaml:
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
Foreground="{Binding ForegroundColorToDisplay, Mode=OneWay}"/>
If you are interested, a value converter would work like this:
Create a class in your code behind (or elsewhere if it's going to be used a lot) that implements IValueConverter, takes the string and returns a Brush:
public class StringToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return GetBrushFromString(value as string)
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
Throw new SomeException();
}
}
and use it in your binding in xaml to convert the selectedItem binding to Foreground brush:
<Window.Resources>
<local:StringToBrushConverter x:Key="converter" />
</Window.Resources>
...
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
Foreground="{Binding FontColor, Mode=OneWay, Converter={StaticResource converter}}"/>
I'm sure this is probably something basic in WPF but I'm new to XAML syntax I'm trying to wrap my head around it.
The Setup
I have a LogItem Type -- just a POCO:
public class LogItem
{
public string Message {get;set;}
public Color MessageColor {get;set;}
}
and a List of LogItem in my ViewModel:
private ObservableCollection<LogItem> _logItems;
public ObservableCollection<LogItem> LogItems
{
get { return _logItems; }
set
{
if (value != _logItems)
{
_logItems = value;
OnPropertyChanged("LogItems");
}
}
}
My viewmodel is bound to the view so that I can do the following:
<ListBox Grid.Row="0" Margin="0,10,0,0" Grid.ColumnSpan="3" Height="150" ItemsSource="{Binding LogItems}">
(Obviously I still have to set the display text binding, etc.)
The Question
Given that I have a Message and MessageColor property in LogItems, what is the correct XAML syntax to bind the color of the item text to the color I specify?
<ListBox Grid.Row="0" Margin="0,10,0,0" Grid.ColumnSpan="3" Height="150" ItemsSource="{Binding LogItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Message}" Foreground="{Binding MessageColor}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
TextBlock Foreground expects a Brush not a Color. Like a lot of things in WPF, There are lot's of ways to approch this. Here is a couple:
Change to MessageColor property in your viewModel to Brush
Brush MessageColor {get;set;}
Create a SolidColorBrush and bind it to your color
<TextBlock Text="{Binding Message}">
<TextBlock.Foreground>
<SolidColorBrush Color="{Binding MessageColor}"/>
</TextBlock.Foreground>
</TextBlock>
Create a ColorToBrushConverter
public class ColorToBrushConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return Brushes.Black; // Default color
Color color = (Color)value;
return new SolidColorBrush(color);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
In xaml, create the converter as static resource
<Window.Resources>
<local:ColorToBrushConverter x:Key="colorToBrushConverter"/>
</Window.Resources>
use it in the binding
<TextBlock Text="{Binding Message}" Foreground="{Binding MessageColor, Converter={StaticResource colorToBrushConverter}"/>
Good luck
So I have a button. I want to set the visibility of the button according to the value of an integer property of a class. This requires a data binding and a converter.
The XAML code for the button is as follows:
<Window.Resources>
<local:Button1VisibilityConverter x:Key="Button1VisibilityConverter"/>
<local:ModeValues x:Key="ModeHolder"/>
</Window.Resources>
<Grid>
<StackPanel HorizontalAlignment="Left" Height="150" Margin="92,90,0,0" VerticalAlignment="Top" Width="301">
<Button Content="1" Height="58" Background="#FFA20000" Foreground="White" Visibility="{Binding Source={StaticResource ModeHolder}, Path=State, Converter=Button1VisibilityConverter}"/>
<Button Content="2" Height="58" Background="#FF16A200" Foreground="White"/>
<Button Content="3" Height="58" Background="#FF4200A2" Foreground="White"/>
</StackPanel>
</Grid>
My converter is as follows:
class Button1VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targettype, object parameter, System.Globalization.CultureInfo culture)
{
int mode = (int)value;
if (mode == ModeValues.Red)
return System.Windows.Visibility.Visible;
else
return System.Windows.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
The class that has the property I want to control the visibility is as follows:
public class ModeValues : IObservable<int>
{
private int _state = -1;
public static int Red
{
get
{
return 0;
}
}
public static int Green
{
get
{
return 1;
}
}
public static int Purple
{
get
{
return 2;
}
}
public int State
{
get
{
return this._state;
}
set
{
this.State = value;
}
}
}
I have no idea why it isn't working. I thought I had to bind the visibility to the property of the instance of the ModeHolder, make the ModeHolder observable, and convert the int to a visibility. What am I missing?
Converter=Button1VisibilityConverter
should be:
Converter={StaticResource Button1VisibilityConverter}