C# WPF Image Converter for Buttons based on Boolean Value - c#

I am making an RPG in WPF and C#. I have movement buttons with images attached. I am trying to figure out how to change the image of the button depending on if there is a room available to move to in that direction. I have looked up converters but I am not quite sure how to implement them for my situation.
This is one example I have tried to implement that I found online:
<Button Content="{Binding MyBooleanValue, Converter={StaticResource
MyBooleanToImageConverter}}" />
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
bool v = (bool)value;
Uri path = new Uri((v ? "ImgSrcIfTrue.png" : "ImgSrcIfFalse.png"), UriKind.Relative);
return new Image()
{
Source = new System.Windows.Media.Imaging.BitmapImage(path),
Height = ...,
Width = ...,
};
}
Here is part of the code I am working on
<!-- Movement Buttons -->
<Button Grid.Row="1" Grid.Column="1"
Click="OnClick_MoveNorth">
<StackPanel>
<Image Source= "/Image/Buttons/Up.png"/>
</StackPanel>
</Button>
I already have functions for the boolean values, i am just trying to figure out how to implement a Converter to change the button image.
I have used the Boolean Visibility and hoping to do something similar.
Visibility="{Binding HasMonster, Converter={StaticResource BooleanToVisibility}}"

Better bind the Source property of an Image element in the Content of the Button:
<Button>
<Image Source="{Binding MyBooleanValue,
Converter={StaticResource MyBooleanToImageConverter}}"/>
</Button>
The converter would directly return a BitmapImage. If the image files are supposed to be assembly resources (i.e. they are part of your Visual Studio project and their Build Action is set to Resource), they must be loaded from Pack URIs:
public class BooleanToImageConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var uri = (bool)value
? "pack://application:,,,/ImgSrcIfTrue.png"
: "pack://application:,,,/ImgSrcIfFalse.png";
return new BitmapImage(new Uri(uri));
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
You would add the converter to the Window's Resources like this:
<Window.Resources>
<local:BooleanToImageConverter x:Key="MyBooleanToImageConverter"/>
...
</Window.Resources>

Related

How to use multibinding in Button width

I'm trying to use a Multibinding in combination with a converter with a Button control and Width property in XAML but I can't get it to work.
The converter is:
public class ColumnsToWidthConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return 40;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
It's hardcoded for 40 now, for testing purposes.
The XAML definition is:
<Button
Height="{Binding ElementName=root,Path=KeyHeight}"
FontSize="{Binding FontSize}"
Content="{Binding Display}"
Command="{Binding ElementName=root, Path=Command}"
CommandParameter="{Binding}"
Style="{StaticResource SelectedButton}">
<Button.Width>
<MultiBinding Converter="{StaticResource ColumnsToWidthConverter}">
<Binding Path="Columns"/>
<Binding Path="KeyHeight" ElementName="root"/>
</MultiBinding>
</Button.Width>
</Button>
The button is rendered from a ListView and defined in the ListView.ItemTemplate. When debugging the application, the converter is passed and the value of 40 is returned. The object[] values parameter contains the correct values passed in the MultiBinding paths. However, the width of the button is set to its content and not the 40 as in the example above.
The ColumnsToWidthConverter is defined in the parent ListView.Resources
<converter:ColumnsToWidthConverter x:Key="ColumnsToWidthConverter"/>
When I remove the MultiBinding and set the Width property to 40 in the XAML definition, the button is rendered correctly.
The root element is the usercontrol itself and KeyHeight is a DependencyProperty.
How do I set the button width using the multibinding?
The issue does not come from the multibinding but from the converter itself. When implementing a converter, you are expected to return the same type of value as expected by the control (there's no implicit conversion since you're the one implementing the converter). In this case, the Width property is a double, so you should return a value of the same type:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return 40d;
}

Use namedSize in onplatform using XAML

I'm trying to use an specific size to the font of an label for each platform using exclusively XAML. This code works fine:
<Label x:Name="DescricaoLabel"
Grid.Row="1"
Grid.Column="0"
HorizontalTextAlignment="Start"
Text="{Binding Descricao}"
TextColor="#426d76">
<Label.Font>
<OnPlatform x:TypeArguments="Font"
Android="14"
iOS="Micro" />
</Label.Font>
</Label>
However, the tag Label.Font is marked as obsolete. I tried this:
<Label.FontSize>
<OnPlatform x:TypeArguments="x:Double"
Android="14"
iOS="Micro" />
</Label.FontSize>
But using the tag Label.FontSize I was unable to compile because of the use of the NamedSize 'Micro'.
What is the best way to use double and also NamedSize within XAML and without make use of an obsolete tag?
The type needs to be of the same type. You cannot have both named size and double together in the default one.
The NamedSize works via an inbuilt static converter. You can write your own converter, maybe taking both as string and then converting. Or use a converter with parameter and specify the value and type.
The default one doesn't handle the scenario you are trying to achieve.
public class StringToSizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double size;
if(double.TryParse(value.ToString, out size))
{
//this is a double
return size;
}
// its a named size, so convert the named size to enum
NamedSize namedSize;
if (Enum.TryParse(value.ToString, true, out namedSize))
{
return Device.GetNamedSize(NamedSize.Default, typeof(Label));
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can read more about Converters below :
Bindings and Collections
How do I use IValueConverters?
Another alternative approach is to define a named label style in your app.cs or app.xaml.cs with the font size set and using that style in your XAML. In that case you need not use OnPlatform in XAML, it will be in your CS file.

Converter only firing once and not every time label content changes

I have label with a width of auto, that is bound to a property of type string.
<Label x:Name="ExampleLabel" Content="{Binding ExampleProperty}"Height="30" Width="Auto" >
I then have a property that is bound to the width of that label. The converter should convert the width to a negative value.
<UserControl.Resources>
<c:PositiveToNegativeConverter x:Key="PositiveToNegativeConverter"/>
</UserControl.Resources>
"{Binding ElementName=ExampleLabel, Path=Width, Converter={StaticResource PositiveToNegativeConverter}}"
I want the converter to execute when the label content changes, but it is only fired once, when the application loads.
Here is my converter
public class PositiveToNegativeConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (double)value * -1;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Math.Abs((double) value);
}
}
Any help is much appreciated. Thanks.
Does it work if you bind to ActualWidth instead of Width? Width is just whatever value you last assigned to the Width property, while ActualWidth is a read-only live-updated runtime value for how wide the thing really is in the UI.
I would expect this to update when you want it to:
"{Binding ElementName=ExampleLabel, Path=ActualWidth, Converter={StaticResource PositiveToNegativeConverter}}"

WPF - Two way binding

I've created user control (toggle button) that contains property called ButtonState (Checked, Disabled or Available). Control has built-in mouse click event which converts its state from available to pressed or from pressed to available (if it is disabled it can't be clicked).
My purpose is create "tree" of buttons, something like that:
OPTION 1 OPTION 2
SUBOPTION1 SUBOPTION1 SUBOPTION1 SUBOPTION1
.... .....
So if I click on button "OPTION 1" (it changes state to PRESSED within control) I would like to button "OPTION 2" go to state DISABLED. If I click on OPTION 1 again, it converts from PRESSED to AVAILABLE and OPTION2 goes to AVAILABLE TOO. The same procedure should be run if I click on OPTION2 (analogously of course). Briefly again: only one button can have PRESSED state and if one has such state other one must be disabled. And if one is available - the other must be available as well.
I've created converter (InverseButtonStateConverter) BUTTONSTATE -> BUTTONSTATE
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
switch (((Controls.ButtonState)value))
{
case Controls.ButtonState.Available: return Controls.ButtonState.Available;
case Controls.ButtonState.Pressed: return Controls.ButtonState.Disable;
case Controls.ButtonState.Disable: return Controls.ButtonState.Available;
default: return Controls.ButtonState.Available;
}
}
Using in XAML :
<Controls:ToggleRectangleButton HorizontalAlignment="Center" VerticalAlignment="Center" Height="109" Width="210" ButtonText2="Bilety jednorazowe" TextFontSize="25" Grid.Column="0" x:Name="btSingleTicket" Click="btSingleTicket_Click" ButtonState="{Binding ElementName=btTimeTicket, Path=ButtonState, Converter={StaticResource InverseButtonStateConverter}}"/>
<Controls:ToggleRectangleButton HorizontalAlignment="Center" VerticalAlignment="Center" Height="109" Width="210" ButtonText2="Bilety czasowe" TextFontSize="25" Grid.Column="3" x:Name="btTimeTicket" Click="btTimeTicket_Click" ButtonState="{Binding ElementName=btSingleTicket, Path=ButtonState, Converter={StaticResource InverseButtonStateConverter}}"/>
When I run this code it works good when I'm clicking on one button. If I interrupt it by clicking on second (it won't work as I would like to) first's button ability to correctyly working disappear.
I've tried to change binding modes but I have no idea how to make it. Some help?
Using two ToggleButtons, and a boolean inverter converter, you can achieve it by binding their respective IsEnabled properties to the IsChecked properties:
<ToggleButton Content="1" x:Name="btn1" IsEnabled="{Binding ElementName=btn2, Path=IsChecked, Converter={StaticResource BooleanInverterConverter}}"/>
<ToggleButton Content="2" x:Name="btn2" IsEnabled="{Binding ElementName=btn1, Path=IsChecked, Converter={StaticResource BooleanInverterConverter}}"/>
And the ValueConverter (simple implementation):
public class BooleanInverterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool) value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Unable to apply string formatting in Silverlight DataGrid column

I have a Data Grid. Its Item source is set to a List. My problem is that Iam unable to apply string formatting . This is formats Ive tried . Am I missing some thing ?
StringFormat='MM/dd/yyyy'
StringFormat={0:dd-MMM-yyyy}
Attached the resultant grid
<sdk:DataGridTemplateColumn Header="Recieved Date" Width="Auto" >
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=RecievedDate, StringFormat=\{0:dd-MMM-yyyy\} }" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<sdk:DatePicker Name="dtpFinancialAndComplianceLog" Text="{Binding Path=RecievedDate,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
If I understand what you're trying to do correctly, you have a DataGrid column which you want to display a DateTime object in a certain format. Ordinarily a DateTime object will sort out its own formatting depending on the System.Threading.Thread.CurrentUICulture.
Easiest way I know of to force any object into a certain format is to use a custom IValueConverter:
namespace MyProject.Converters
{
public class FormatConverter : IValueConverter
{//Suitable only for read-only data
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return string.Empty;
if(string.IsNullOrEmpty(parameter.ToString()))
return value.ToString();
return string.Format(culture, parameter.ToString(), value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
After adding a namespace to your xaml: xmlns:conv="clr-namespace:MyProject.Converters" and declaring your converter in the control's resources <conv:FormatConverter x:Key="Formatter" />, you will need to bind your column's data using your new converter:
<TextBlock Text="{Binding Path=RecievedDate, Converter={StaticResource Formatter}, ConverterParameter=\{0:dd-MMM-yyy\} }" />

Categories