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");
}
}
Related
I need access to the entry component that the converter is attached to, so I can change the cursor position with Entry.CursorPosition.
I have a BindableObject that is also an IValueConverter, how can I get to the Entry
public class MaskConverter : BindableObject, IValueConverter
{
...
}
In XAML name the Entry and use x:Reference:
Converter with MyEntry property (ConvertBack for Mode=TwoWay not shown):
public class MyConverter : BindableObject, IValueConverter
{
public Entry MyEntry { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var entry = MyEntry;
Debug.WriteLine($"convert:pos:{entry?.CursorPosition}:");
return (string)value;
}
...
}
XAML using MyEntry property (MyText is a property on the viewmodel, not shown):
<ContentPage.Resources>
<local:MyConverter x:Key="myConverter" MyEntry="{x:Reference myEntry}" />
...
</ContentPage.Resources>
...
<Entry x:Name="myEntry" Text="{Binding MyText,
Converter={StaticResource myConverter}}">
Without the MyEntry property the named Entry might be passed to the converter using ConverterParameter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var param = parameter as Entry;
Debug.WriteLine($"convert:pos:{param?.CursorPosition}:");
return (string)value;
}
XAML when passing the named Entry in ConverterParameter:
<ContentPage.Resources>
<local:MyConverter x:Key="myConverter" />
...
</ContentPage.Resources>
...
<Entry x:Name="myEntry" Text="{Binding MyText,
Converter={StaticResource myConverter},
ConverterParameter={x:Reference myEntry}}">
Using a wpf ListBox I'm trying to display a list of filename without displaying the full path (more convenient for user).
Data comes from an ObservableCollection which is filled using Dialog.
private ObservableCollection<string> _VidFileDisplay = new ObservableCollection<string>(new[] {""});
public ObservableCollection<string> VidFileDisplay
{
get { return _VidFileDisplay; }
set { _VidFileDisplay = value; }
}
In the end I want to select some items and get back the full file path. For this I have a converter :
public class PathToFilenameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//return Path.GetFileName(value.ToString());
string result = null;
if (value != null)
{
var path = value.ToString();
if (string.IsNullOrWhiteSpace(path) == false)
result = Path.GetFileName(path);
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
Which I bind to my listbox itemsource :
<ListBox x:Name="VideoFileList" Margin="0" Grid.Row="1" Grid.RowSpan="5" Template="{DynamicResource BaseListBoxControlStyle}" ItemContainerStyle="{DynamicResource BaseListBoxItemStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding Path=DataContext.VidFileDisplay, Converter={StaticResource PathToFileName},ElementName=Ch_Parameters, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Path=SelectedVidNames,ElementName=Ch_Parameters, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
Without the converter, it is working fine (but of course this is the full path displayed in the listbox). With the converter I have one character per line... displaying this :
System.Collections.ObjectModel.ObservableCollection`1[System.String]
Where am I wrong ?
Thank you
In ItemsSource binding converter applies to the whole list and not to each item in the collection. If you want to apply your converter per item you need to do it ItemTemplate
<ListBox x:Name="VideoFileList" ItemsSource="{Binding Path=DataContext.VidFileDisplay, ElementName=Ch_Parameters}" ...>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=., Converter={StaticResource PathToFileName}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
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
I would like to use static texts fetched from a web service in my WP7 app. Each text has a Name (the indetifier) and a Content property.
For example a text could look like this:
Name = "M43";
Content = "This is the text to be shown";
I would then like to pass the Name (i.e. the identifier) of the text to an IValueConverter, which would then look up the the Name and return the text.
I figured the converter to look something like this:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(value)).Content;
}
return null;
}
}
Then in the XAML:
<phone:PhoneApplicationPage.Resources>
<Helpers:StaticTextConverter x:Name="StaticTextConverter" />
</phone:PhoneApplicationPage.Resources>
...
<TextBlock Text="{Binding 'M43', Converter={StaticResource StaticTextConverter}}"/>
However, this does not seem to work and I am not sure that I pass in the value to the converter correctly.
Does anyone have some suggestions?
I finally found the answer. The answer was a mix between that of #Shawn Kendrot and another question I asked here: IValueConverter not getting invoked in some scenarios
To summarize the solution for using the IValueConverter I have to bind my control in the following manor:
<phone:PhoneApplicationPage.Resources>
<Helpers:StaticTextConverter x:Name="TextConverter" />
</phone:PhoneApplicationPage.Resources>
<TextBlock Text="{Binding Converter={StaticResource TextConverter}, ConverterParameter=M62}" />
Since the ID of the text is passed in with the converter parameter, the converter looks almost the same:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null && parameter is string)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(parameter)).Content;
}
return null;
}
}
However, as it turns out, the binding and thus the converter is not invoked if it does not have a DataContext. To solve this, the DataContext property of the control just has to be set to something arbitrary:
<TextBlock DataContext="arbitrary"
Text="{Binding Converter={StaticResource TextConverter}, ConverterParameter=M62}" />
And then everything works as intended!
The problem lies in your binding. It will check the DataContext, and on this object, it will try to evaluate the properties M62 and ValueboxConsent on that object.
You might want to add static keys somewhere in your application where you can bind to:
<TextBlock Text="{Binding Source="{x:Static M62.ValueboxConsent}", Converter={StaticResource StaticTextConverter}}" />
Where M62 is a static class where your keys are located.. like so:
public static class M62
{
public static string ValueboxConsent
{
get { return "myValueBoxConsentKey"; }
}
}
If you want to use a value converter, you'll need to pass the string to the parameter of value converter
Xaml:
<TextBlock Text="{Binding Converter={StaticResource StaticTextConverter}, ConverterParameter=M43}"/>
Converter:
public class StaticTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter != null)
{
return App.StaticTexts.Items.SingleOrDefault(t => t.Name.Equals(parameter)).Content;
}
return null;
}
}
xmlns:prop="clr-namespace:MyProj.Properties;assembly=namespace:MyProj"
<TextBlock Text="{Binding Source={x:Static prop:Resources.MyString}, Converter={StaticResource StringToUpperCaseConverter}}" />
I have a collection databound to a ListBox. What I would like to do is show some UI based on whether or not some property of the member of the collection exists.
E.g.:
public class Widget
{
public string foo;
public string bar;
}
public ObservableCollection<Widget> Stuff;
XAML:
<ListBox ItemsSource="{Binding Stuff}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding foo}"
Visiblity="{Binding
(foo != null ? Visibility.Visible : Visibility.Collapsed)
}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note the Visibility attribute on the TextBlock. Clearly this isn't supported, but it should give you an idea of what I want to do.
One possible solution is that I could add a property to widget that looks like this:
public Visibility has_foo;
And then:
... Visibility="{Binding has_foo}" ...
But it seems awkward to have to generate these additional properties.
I suspect there is a much better way. Is there? How would you do it?
Thanks.
Create a value converter. Something like
public class NullToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then add it something like
<YourUserControl.Resources>
<NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
</YourUserControl.Resources>
<ListBox ItemsSource="{Binding Stuff}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding foo}"
Visiblity="{Binding foo,
Converter={StaticResource NullToVisibilityConverter}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Obviously I've not implemented ConvertBack (not really sure if you will be able to convert back) but you shouldn't need it in this instance.