Sweepster
Sweepster

Reputation: 1949

Setting default value in XAML Multibinding StringFormat

I am new to XAML and I have to use it to modify the UI of a frontend system. The fields are fixed and I cannot modify them, therefore I need a creative solution to my problem that frankly, I don't have the skill to figure out despite my best efforts.

The UI is for displaying attributes of videogames in a database. Specifically, I'm interested in the part of the UI that currently allows for displaying an image of the video game's controller (different games use different controllers).

As is, the UI only allows for displaying a controller for every videogame belonging to a platform as demonstrated with the below code:

<TextBlock x:Name="PlatformControlPanel" Visibility="Collapsed">
    <TextBlock.Text>
         <MultiBinding StringFormat="{}pack://siteoforigin:,,,/Themes/Custom/Images/Controls/{0}/{0}.png">
             <Binding Path="SelectedGame.Platform" />
         </MultiBinding>
    </TextBlock.Text>
</TextBlock>
<Image Source="{Binding Text, ElementName=PlatformControlPanel}" RenderOptions.BitmapScalingMode="HighQuality" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="Uniform"/>

In the example above, the controller image is stored in the following directory format: /Themes/Custom/Images/Controls/"Platform Name"/"Platform Name".png

And this works super well but the problem is that a given platform can have more than one controller type such as joystick controllers or GamePad controllers.

So what I want to do is change the code so that it displays a default controller for a given platform but display a different controller for specific games that use a different controller type.

To do this, because I cannot set new fields in the database, I modified the code to display a controller based on the name of the game:

<TextBlock x:Name="PlatformControlPanel" Visibility="Collapsed">
    <TextBlock.Text>
         <MultiBinding StringFormat="{}pack://siteoforigin:,,,/Themes/Custom/Images/Controls/{1}/{0}.png">
              <Binding Path="SelectedGame.Title" />
              <Binding Path="SelectedGame.Platform" />
         </MultiBinding>
     </TextBlock.Text>
</TextBlock>
<Image Source="{Binding Text, ElementName=PlatformControlPanel}" RenderOptions.BitmapScalingMode="HighQuality" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="Uniform"/>

At this point, the UI looks to /Themes/Custom/Images/Controls/"Platform Name"/"Game Title".png

The problem is, the UI is only showing an image if "Game Title".png exists. Otherwise, it shows nothing.

I don't know how to set a default value so that if "Platform Name"/"Game Title".png doesn't exist, the UI will fallback on "Platform Name"/"Platform Name".png.

Can someone help me?

Edit: Xaml update (unrelated code omitted, no errors reported when building):

<UserControl xmlns:converter="clr-namespace:BigBoxTheme.Views.Converters"

<!--Main Content Row-->
        <Grid Grid.Row="2">
            <Grid.Resources>
                <converter:MultiValueConverter x:Key="MultiValueConverter"/>
            </Grid.Resources>

<Image RenderOptions.BitmapScalingMode="HighQuality" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="Uniform">
                    <Image.Source>
                        <MultiBinding Converter="{StaticResource MultiValueConverter}">
                            <Binding Path="selectedExample"/>
                            <Binding Path="ValidationTest"/>
                        </MultiBinding>
                    </Image.Source>
                </Image>

C# code:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media.Imaging;

namespace BigBoxTheme.Views.Converters
{
class MultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        BitmapImage result = null;
        //process the variables passed in
        Uri SourceUri = new Uri($"pack://siteoforigin:,,,/Themes/Custom/Images/Controls/{values[1]}/{values[0]}.png", UriKind.Absolute);//use either string format or whatever you like to create a valid Uri
        if (File.Exists(SourceUri.AbsolutePath))
        {
            //we have found the image
            //no need to do anything just let it run through
        }
        else
        {
            //use the default
            SourceUri = new Uri($"pack://siteoforigin:,,,/Themes/Custom/Images/Controls/{values[1]}/{values[1]}.png", UriKind.Absolute);
        }
        return new BitmapImage(SourceUri);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
}

Upvotes: 1

Views: 877

Answers (1)

XAMlMAX
XAMlMAX

Reputation: 2363

In your Binding use TargetNullValue like so:

<Binding Path="SelectedGame.Title" TargetNullValue="Platform Name" />  

EDIT
After our conversation in comments I would recommend you use a converter, to be more precise a Multi Value Converter. It would look like this:

class MultiValueConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        BitmapImage result = null;
        //process the variables passed in
        Uri SourceUri = new Uri($"pack://siteoforigin:,,,/Themes/Custom/Images/Controls/{values[1]}/{values[0]}.png", UriKind.Absolute);//use either string format or whatever you like to create a valid Uri
        if (File.Exists(SourceUri.AbsolutePath))
        {
            //we have found the image
            //no need to do anything just let it run through
        }
        else
        {
             //use the default
             SourceUri = new Uri($"pack://siteoforigin:,,,/Themes/Custom/Images/Controls/{values[1]}/{values[1]}.png", UriKind.Absolute);
        }
        return new BitmapImage(SourceUri);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}  

you would then use it like this:

<Image>
    <Image.Source>
        <MultiBinding Converter="{StaticResource MultiValueConverter}">
            <Binding Path="SelectedExample"/>
            <Binding Path="ValidationTest"/>
        </MultiBinding>
    </Image.Source>
</Image>  

EDIT 2
The converter is in its own file, say MultiValueConverter.cs, it would go where your views are, preferably in a Folder called Converters. Now in the xaml at the top you would include that converter like this:

xmlns:converter="clr-namespace:SO_app.Converters"//clr-namespace is the namespace of your view followed by a dot to access the Converters folder  

Now inside of your Window or UserControl resources tag like this:

<Window.Resources>
    <converter:MultiValueConverter x:Key="MultiValueConverter"/>
</Window.Resources>

Upvotes: 2

Related Questions