Is it possible to concatenate an imagesourece Uri in xaml - wpf

Hi I've been looking at ways to dynamically change an image Uri in xaml and in my research came across the following answer, which has certainly given me hope that what I really want to do might just be possible. In the original question the questioner wanted to swap the image itself, in my case I want to swap the directory where the image is located.
So when one looks at the answer that #Clemens provided one ends up with an images source being bound to a dependency property that is dynamically set when the form loads.
What I'd like to know is whether it would be feasible to set the location part of the uri dynamically (as per the logic that #Clemens is advocating and then simply append the Image name in the actual binding statement so that it might look something like this:
<Image Source="{Binding ImageUri & myImage.png}"/>
Essentially I have a number of buttons to which I would like to assign a default image og a size to be determined by the end user. To that end the Images would be stored in different folders in the application (in fact its a custom control) and then the relevant path bit of the URI would be set as per the suggestion in the referenced answer and I'd just append the Image name (which would be the same for the button irrespective of the size) and it would then have the correct image to display.

MainWindow.xaml.cs :
namespace MainWindowNamespace
{
public sealed class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
string fullPath = Path.GetFullPath((string)value);
return new BitmapImage(new Uri(fullPath));
}
catch { return null; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{ throw new NotImplementedException(); }
}
}
MainWindow.xaml :
<Window
xmlns:imgConvert="clr-namespace:MainWindowNamespace">
<Window.Resources>
<imgConvert:ImageConverter x:Key="ImageConverter" />
</Window.Resources>
<Image ImageSource="{Binding ImagePath, Converter={StaticResource ImageConverter}}" />
</Window>

Related

How to properly compute URI from ValueConverter in WPF

I have a simple application where I want to swap out image based on a value converter. Upon testing the images shows fine when directly bound to the code behind of a user control.
public partial class HypercombControls : Grid
{
public static readonly DependencyProperty MoveImageSourceProperty =
DependencyProperty.Register("MoveImageSource", typeof(Uri), typeof(HypercombControls),
new PropertyMetadata(new Uri("pack://application:,,/Media/control-bar/move.png")));
...
}
<Button Click="MoveButton_OnClick" Style="{StaticResource DragModeButtonStyle}">
<Image Source="{Binding ElementName=HyperControls, Path=MoveImageSource}">
</Image>
</Button>
The image shows as expected. I then added a MultiBinding with a MultiValueConverter to change the image source dynamically based on some logic. The image always comes up blank when I return the URI even if I return the exact same image URI as constructed initially. Is there something I am missing?
public object Convert(object[] values, Type targetType, object parameter,
CultureInfo culture)
{
// logic omitted but tested with the following ouput.
// same as new PropertyMetadata(...)
return new Uri("pack://application:,,/Media/control-bar/move.png");
}

Writing conditional statements in XAML code

I have this listBox that gets populated, each item can be either male or female depending on the 'SEX' property that is binded to the listBox. (Could be either 'M' for male and 'F' for female)...
For each item i would like to display either a male or female symbol based on the list items SEX property.
for instance if "{Binding SEX}" equals 'M':
<Image Source="../Images/male48.png" Visibility="Visible" />
and if "{Binding SEX}" equals 'F':
<Image Source="../Images/female48.png" Visibility="Visible" />
How exactly would I go about getting this to work?
A common approach to this problem is to create a value converter, this converts the value returned by a binding into some other value that relates to the property of a UI control.
You can create a converter that takes the sex and maps it to an image source:
public class SexToSourceConverter : IValueConverter
{
public object Convert(object value, string typeName, object parameter, string language)
{
string url = ((string)value == "M") ? "../Images/male48.png" : "../Images/female48.png";
return new BitmapImage(new Uri(url , UriKind.Relative));
}
public object ConvertBack(object value, string typeName, object parameter, string language)
{
throw new NotImplementedException();
}
}
Using it in your XAML as follows:
<Image Source="{Binding Path=Sex, Converter={StaticResource SexToSourceConverter }" />
If someone is interested in how this could work, I've made a solution based on ColinE's answer. First, you've to create a new class which contain the conditions you'll like to add to the XAML code:
public class MyNiceConverterName : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
// Your conditions here!
return value_you_want_to_return; // E.g., a string, an integer and so on
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException(); // Leave this like here, don't worry
}
}
Call the class whatever you want (right now it's called MyNiceConverterName) and implement the Convert() method with the conditions you'd like to add to the XAML file. Remember to cast the object value to the type you're using (e.g., (int)value if it's an integer).
This is almost done! But not yet, first declare the converter in your XAML as a resource. You can paste this code below the namespaces declaration:
<Control.Resources>
<converter:MyNiceConverterName xmlns:converter="clr-namespace:My_Namespace" x:Key="MyNiceConverterName" />
</Control.Resources>
You've to declare the namespace where you defined the class (i.e., remove My_Namespace with yours') and also rename MyNiceConverterName to your class name. The key will be the name defined to reference the converter within the XAML document, here I've used the same class name but you can freely change it.
Finally, it's time to use the converter. Put this and you're done:
{Binding variable_with_value, Converter={StaticResource MyNiceConverterName}}
Remember to change variable_with_value with the one you'd like to use within your binding.
I hope it helps!
Either use a binding converter or use two triggers.
For Siverlight this is the correct IValueConverter link, I am not sure if triggers are supported.

Silverlight Custom Button content

What would be a good way to have the text (content) of buttons per-client configurable in a SL 4 app? I'm still pretty novice w/ SL so this may seem trivial.
The issue isn't new. The system currently has a static XAML attribute for ButtonA's content as "Do Stuff" (Content="DoStuff"). Now one client wants that to read "Do Things". This will continue to crop up on occasion in arbitrary places across the system.
I have a dictionary available that will contain the custom text, but would LIKE (if possible) to be able to have a default value and only override if there is a dictionary entry.
Conceptually, it would be nice to be able to have:
<Button Content="Do Stuff" OverrideContentKey="ButtonAOverrideContent" />
where if the dictionary has a key of ButtonAOverrideContent, it will override it, but otherwise "Do Stuff" will show.
Is there a way to perhaps write a converter and make some entries in App.xaml that would then allow all buttons to conditionally override the content? What I've seen of converters looks like there's not a smooth way to pass information about the control (e.g. the override key) to them.
You can use a ConverterParameter property of a Binding to pass your override content key to a converter.
public class ReplaceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string key = (string)parameter;
var someDictionary = GetYourReplacementDictionary();
if (someDictionary.ContainsKey(key))
{
return someDictionary[key];
}
else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your App.Xaml resources:-
<local:ReplaceConverter x:Key="replacer" />
Then on a button:-
<Button Content="{Binding Source='Do Stuff', ConverterParameter=ButtonAOverrideContent, Converter={StaticResource replacer}}" />

WPF Image Source binding with StringFormat

I'm new to WPF and MVVM (started this week experimenting with it) and trying to bind image resources at runtime. The items I'm trying to display contain an enumerate property that indicates the type or state of the item:
public class TraceEvent
{
/// <summary>
/// Gets or sets the type of the event.
/// </summary>
/// <value>The type of the event.</value>
public TraceEventType EventType { get; set; }
}
As far as I known the Source attribute of Image has a value converter that takes strings and returns Uri objects.
<Image Source="{Binding Path=EventType, StringFormat={}/AssemblyName;component/Images/{0}Icon.ico}" />
So why doesn't the above work? If I enter the uri directly (without binding) the image is shown perfectly. In fact, if I do the binding in a TextBlock and use the result of that value in the Image also shown without problem:
<TextBlock Visibility="Collapsed" Name="bindingFix" Text="{Binding Path=EventType, StringFormat={}/AssemblyName;component/Images/{0}Icon.ico}"/>
<Image Source="{Binding ElementName=bindingFix, Path=Text}" />
I'm pretty sure I'm doing something terrible wrong for such an obvious thing to do with images.
Thanks.
StringFormat is only used if the target property is actually a string - the Image.Source property is a Uri so the binding engine won't apply the StringFormat.
One alternative is to use a Value Converter. Either write a general StringFormatConverter that takes the string format in the ConverterParameter, or a more specific ImageSourceConverter e.g.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.Format( "/Images/{0}Icon.ico", value );
}
Note that if your images live in the same assembly as they are used, then you shouldn't need to specify the assembly name in the URI and the above syntax should work.
i'm not sure about this one but it seems that you are passing the image's source property a string where it expects a uri. so, you have to convert your string into a uri object

WPF - Dynamically access a specific item of a collection in XAML

I have a data source ('SampleAppearanceDefinitions'), which holds a single collection ('Definitions'). Each item in the collection has several properties, including Color, which is what I'm interested in here.
I want, in XAML, to display the Color of a particular item in the collection as text. I can do this just fine using this code below...
Text="{Binding Source={StaticResource SampleAppearanceDefinitions}, Path=Definitions[0].Color}"
The only problem is, this requires me to hard-code the index of the item in the Definitions collection (I've used 0 in the example above). What I want to do in fact is to get that value from a property in my current DataContext ('AppearanceID'). One might imagine the correct code to look like this....
Text="{Binding Source={StaticResource SampleAppearanceDefinitions}, Path=Definitions[{Binding AppearanceID}].Color}"
...but of course, this is wrong.
Can anyone tell me what the correct way to do this is? Is it possible in XAML only? It feels like it ought to be, but I can't work out or find how to do it.
Any help would be greatly appreciated!
Thanks!
AT
MultiBinding is your friend here:
Assuming you have a TextBlock:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource AppearanceIDConverter}">
<Binding Source="{StaticResource SampleAppearanceDefinitions}" />
<Binding Path="AppearanceID" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
And define a MultiValueConverter to return what you wish to see:
public class AppearanceIDConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<item> items = (List<item>)values[0]; //Assuming its items in a List
int id = (int)values[1]; //Assuming AppearanceID is an integer
return items.First(i => i.ID == id).Color; //Select your item based on the appearanceID.. I used LINQ, but a foreach will work just fine as well
}
public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
Of course, you will need to set the converter as a resource in your Resource dictionary, like you did SampleAppearanceDefinitions. You can also ditch the multibinding and use a regular binding to AppearanceID with a IValueConverter, if you can get to the SampleAppearanceDefinitions collection through code ;).
Hope this helps
Even if it could be possible you'd better not do that this way, but instead use a dedicated property in your view model or in the code behind of your view if it has only a pure graphical meaning.
This property, say "CurrentAppearance", would expose a Color property you could bind from your Xaml :
Text="{Binding CurrentAppearance.Color}"
which is more understandable.
As a general advice : avoid to spoil your Xaml with plumbing code : Xaml should be as readable as possible,
particularly if you work with a team of designers that have no coding skills and do not want to be concerned with the way you are managing the data.
Moreover, if later you decide to change the way data are managed you would not have to change your Xaml.
MultiBinding might actually work if your list is on a viewmodel instead of a staticresource. I was suprised myself to see that the object passed on to the view is actually a pointer to the object on the model, so changing the object in the view (eg. typing in new test in the textbox) directly affects the model object.
This worked for me. The ConvertBack method is never useed.
public class PropertyIdToPropertyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2)
{
var properties = values[0] as ObservableCollection<PropertyModel>;
if (properties != null)
{
var id = (int)values[1];
return properties.Where(model => model.Id == id).FirstOrDefault();
}
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Resources