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>
I have a TextBlock inside a custom user control that I would like to be slightly larger (maybe 7% larger) than the global font size property for that user control. I am unsure of the best way to go about this. Does anyone have any suggestions?
(Obviously this attempt is atrocious, but hopefully it helps visualize what I'm asking).
<TextBlock
x:Name="Title"
FontSize="{myUserControl.FontSize * 1.07}">
Hello Custom User Control!
</TextBlock>
The best answer (credit to #Kenny) is a simple converter that takes the user control font size as it's input.
Use in xaml:
<z:RatioConverter x:Key="AdjustTitleFontSizeConverter" Ratio="1.07" />
<TextBlock
x:Name="Title"
FontSize="{Binding FontSize, Converter={StaticResource AdjustTitleFontSizeConverter}">
Hello Custom User Control!
</TextBlock>
RatioConverter.cs
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
public class RatioConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Input santize first..
return (System.Convert.ToDouble(value)) * this.Ratio;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public Double Ratio
{
get { return (Double)GetValue(RatioProperty); }
set { SetValue(RatioProperty, value); }
}
public static readonly DependencyProperty RatioProperty = DependencyProperty.Register(
"Ratio", typeof(Double), typeof(RatioConverter), new FrameworkPropertyMetadata(1.0));
}
Apply ScaleTransform with a desired scale factor.
In this example all TextBlock inherit FontSize=20 from parent Window (it is Dependency Property inheritance). Then I change FontSize to 22 for one TextBlock, and scale another (20 * 1.1 == 22). They look similar to me.
<Window x:Class="WpfDemos.FontWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WpfDemos" Height="300" Width="300" FontSize="20">
<StackPanel>
<TextBlock Text="Hello Custom User Control!"/>
<TextBlock Text="Hello Custom User Control!"/>
<TextBlock Text="Hello Custom User Control!">
<TextBlock.LayoutTransform>
<ScaleTransform ScaleX="1.1" ScaleY="1.1"/>
</TextBlock.LayoutTransform>
</TextBlock>
<TextBlock Text="Hello Custom User Control!" FontSize="22"/>
<TextBlock Text="Hello Custom User Control!"/>
<TextBlock Text="Hello Custom User Control!"/>
</StackPanel>
</Window>
You can use https://www.nuget.org/packages/CalcBinding/ library for this.
It seems it is not possible to define geometry in a ResourceDictionary in Silverlight. So I'm using Path to store some geometries. Here is the Path.xaml file:
<ResourceDictionary ///some namespaces///>
<Path x:Key="path1" Data="//some geometry//" />
<Path x:Key="path2" Data="//some geometry//" />
<Path x:Key="path3" Data="//some geometry//" />
</ResourceDictionary>
Having reference this ResourceDictionary, I want to use these geometries somewhere in a UserControl:
<Path Data = "{Binding Source={StaticResouce path1}", Path=Data}"/>
But I'm getting the following error:
Value does not fall within the expected range.
How can I get those geometries in the XAML?
Correct you can't. I came across this problem and got round it by having the path strings as properties of a class and then adding that class as a resource. For example here is the class
public class IconPaths
{
public string CircleIcon
{
get { return "M50.5,4.7500001C25.232973,4.75 4.75,25.232973 4.7500001,50.5 4.75,75.767029 25.232973,96.25 50.5,96.25 75.767029,96.25 96.25,75.767029 96.25,50.5 96.25,25.232973 75.767029,4.75 50.5,4.7500001z M50.5,0C78.390381,0 101,22.609621 101,50.5 101,78.390381 78.390381,101 50.5,101 22.609621,101 0,78.390381 0,50.5 0,22.609621 22.609621,0 50.5,0z"; }
}
public string LogIcon
{
get { return "M16.6033,19.539C18.922133,19.539 20.042,21.777275 20.042,23.919949 20.042,26.367331 18.793436,28.397999 16.5877,28.397999 14.394864,28.397999 13.149,26.334829 13.149,24.032551 13.149,21.665472 14.301367,19.539 16.6033,19.539z M5.3724453,18.578932L5.3724453,29.357607 11.370038,29.357607 11.370038,28.189699 6.7645523,28.189699 6.7645523,18.578932z M28.522547,18.46693C24.908003,18.46693 22.700978,20.817846 22.685377,24.032669 22.685377,25.711081 23.260882,27.151291 24.189095,28.045797 25.244007,29.053604 26.587723,29.469608 28.217943,29.469608 29.67366,29.469608 30.905476,29.101805 31.529183,28.877802L31.529183,23.696165 27.97814,23.696165 27.97814,24.816576 30.169766,24.816576 30.169766,28.029497C29.848162,28.189699 29.225756,28.317898 28.314345,28.317898 25.803812,28.317898 24.156695,26.703287 24.156695,23.968267 24.156695,21.265749 25.867714,19.634937 28.490048,19.634937 29.57736,19.634937 30.297468,19.84334 30.872776,20.097841L31.20888,18.963034C30.745175,18.739132,29.770062,18.46693,28.522547,18.46693z M16.666903,18.40313C13.788068,18.40313 11.661542,20.641445 11.661542,24.064671 11.661542,27.326992 13.660466,29.534107 16.506901,29.534107 19.258234,29.534107 21.510861,27.567295 21.510861,23.856268 21.510861,20.657745 19.609839,18.40313 16.666903,18.40313z M14.433776,2.6588883C12.967757,2.6588886,11.773743,3.8522573,11.773743,5.319067L11.773743,15.361408 34.872822,15.361408C35.828533,15.361408,36.603447,16.136215,36.603447,17.092621L36.603447,30.392214C36.603447,31.347221,35.828533,32.121925,34.872822,32.121925L11.773743,32.121925 11.773743,37.347263C11.773743,38.814075,12.967757,40.007481,14.433776,40.007481L35.931637,40.007481C37.397755,40.007481,38.59177,38.814075,38.59177,37.347263L38.59177,12.690889 29.754463,12.690889C29.022553,12.690889,28.424946,12.092585,28.424946,11.36008L28.424946,2.6601186 28.424946,2.6588883z M14.433776,0L29.723061,0 41.251999,11.530681 41.251999,37.347263C41.251999,40.280281,38.866474,42.667,35.931637,42.667L14.433776,42.667C11.498941,42.667,9.1135704,40.280281,9.1135704,37.347263L9.1135704,32.121925 1.7293315,32.121925C0.7749116,32.121925,1.9777951E-07,31.347221,0,30.392214L0,17.092621C1.9777951E-07,16.136215,0.7749116,15.361408,1.7293315,15.361408L9.1135704,15.361408 9.1135704,5.319067C9.1135704,2.3854568,11.498941,0,14.433776,0z"; }
}
}
And here is it called in a resource dictionary
<LocalStyles:IconPaths x:Key="IconPaths"/>
And finaly used in a path
<Path Data="{Binding Source={StaticResource IconPaths}, Path=ArrowIcon}" Height="10" Stretch="Fill" Fill="DimGray" VerticalAlignment="Center" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0.5">
I want to set the image position via code to fit on the button (see screenshot). But I can't work it out. In WinForms it was easy, but in Silverlight I can't just set the X & Y apparently.
public void LockControls()
{
int LockIndex = 0;
DependencyObject myUserControl = LayoutRoot;
foreach (var button in FindAll<Button>(myUserControl))
{
if (button.Tag != null)
{
Image LockedIcon = new Image();
LockedIcon.Width = 20;
LockedIcon.Height = 20;
//LockedIcon.Margin = new Thickness(0,0,0,0);
LockedIcon.Source = new BitmapImage(new Uri("images/LockedIconx20alpha.png", UriKind.Relative));
LockedIcon.Name = "Lockie" + LockIndex;
LayoutRoot.Children.Add(LockedIcon);
button.Tag = "Locked" + LockIndex;
LockIndex++;
}
}
}
http://puu.sh/wS7g
THe screenshot shows the image position (the locck), but I don't understand how the current position is being set. Just to clarify, I want to set the position to the "0%" button
Thanks in advance,
Jack
That's not the correct way of doing. If I understand correctly you want to super-impose an image on top of a button to prevent the user from using it. It won't work.
For that you have to understand the layout system of Silverlight: the controls are laid out by the engine during the measure and arrange events.
Trying to overlay an image like that will require you to hook up on those events, or derive the Button class and override the Arrange method to overlay your image.
But that won't prevent the user from using the button because the button itself is not disabled, and one could just "tab" into it, and activate it.
Instead I suggest you use a style for the button, and override, say, the Disabled state to overlay your locked image. The button style is described here.
All you have to do is replace:
<Rectangle x:Name="DisabledVisualElement" RadiusX="3" RadiusY="3" Fill="#FFFFFFFF" Opacity="0" IsHitTestVisible="false" />
by
<Image x:Name="DisabledVisualElement" IsHitTestVisible="false" Opacity="0" Width="20" Height="20" Source="images/LockedIconx20alpha.png" />
And set the opacity in the following to 1:
<vsm:VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" To=".55"/>
</Storyboard>
</vsm:VisualState>
Set this style on your button (I assume you know how to do this) then when you need to lock your button, set it to disabled and your image will be automatically laid on-top and your button will be un-clickable.
In general, if you want to set the Image position arbitrary, you should host the Image control in a Canvas container.
However, in your case you should really change the Content element of your Button depending on the need for showing the lock or not.
<UserControl DataContext="{Binding Main, Source={StaticResource Locator}}
<Grid.Resources>
<converters:VisibilityConverter x:Key="VisibilityConverter" />
</Grid.Resources>
<Button Width="100" Height="23" IsEnabled="{Binding IsControlsEnabled}">
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="lock.png" Margin="10,0,10,0"
Visibility="{Binding IsControlsEnabled, Converter={StaticResource VisibilityConverter}}"/>
<TextBlock Text="Button"/>
</StackPanel>
</Button.Content>
</Button>
Additionally, you shouldn't really write the kind of code you have in your question in Silverlight. Learn how to use data binding. It's very powerful. Simply bind the IsEnabled property of your Buttons to an exposed Property instead.
An example of doing so using the MVVM Light toolkit (I recommend you learn the MVVM pattern for Silverlight/WPF development):
The View Model:
public class MainViewModel : ViewModelBase
{
private bool isControlsEnabled;
public bool IsControlsEnabled
{
get { return isControlsEnabled; }
set
{
if (IsControlsEnabled.Equals(value)) return;
isControlsEnabled = value;
RaisePropertyChanged(() => IsControlsEnabled);
}
}
}
The Visibility Converter:
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then you can simply enable/disable all controls that are bound, for example using a CheckBox:
<CheckBox IsChecked="{Binding IsControlsEnabled, Mode=TwoWay}" Content="Controls are enabled"/>
I am looking to get a specific behavior on TextBlock so that its height only includes the height of the capital letters (from baseline to top minus "ascender height"). Please see the image Sphinx from Wikipedia to see what I mean. Also the image below may indicate better what I am after.
I am not specifically looking for a pure XAML solution (probably impossible) so a C# code behind (a converter) is also fine.
This is the XAML used in XamlPad to produce the left A in the image above.
<TextBlock Text="A" Background="Aquamarine" FontSize="120" HorizontalAlignment="Center" VerticalAlignment="Center" />
u can try to use attribute LineStackingStrategy="BlockLineHeight" and a Converter on the LineHeight attributes and a converter on the Height of TextBlock.
This a sample code of converters
// Height Converter
public class FontSizeToHeightConverter : IValueConverter
{
public static double COEFF = 0.715;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (double)value * COEFF;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
// LineHeightConverter
public class FontSizeToLineHeightConverter : IValueConverter
{
public static double COEFF = 0.875;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return double.Parse(value.ToString()) * COEFF;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The Coefficient used on converters depends on Used Family Fonts (Baseline and LineSpacing):
<TextBlock Text="ABC" Background="Aqua" LineStackingStrategy="BlockLineHeight"
FontSize="{Binding ElementName=textBox1, Path=Text}"
FontFamily="{Binding ElementName=listFonts, Path=SelectedItem}"
Height="{Binding RelativeSource={RelativeSource Self}, Path=FontSize, Mode=OneWay, Converter={StaticResource FontSizeToHeightConverter1}}"
LineHeight="{Binding RelativeSource={RelativeSource Self}, Path=FontSize, Converter={StaticResource FontSizeToLineHeightConverter}}"/>
The best solution is to find how to calculate the Coeff based on parameters Baseline and LineSpacing of the FontFamily.
In this sample (Segeo UI) the Coeff of Height = 0.715 and LineHeight = 0,875 * FontSize.
Updated:
If I understand right, there's a few tricks I know for this,
You can Scale it with RenderTransform which is usually the most efficient way;
<TextBlock Text="Blah">
<TextBlock.RenderTransform>
<CompositeTransform ScaleY="3"/>
</TextBlock.RenderTransform>
</TextBlock>
Or you can embed the TextBlock in a Viewbox to "zoom" the text to fit the bounds of its container if for example you set hard height values on grid rows like;
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="120"/>
<RowDefinition Height="120"/>
</Grid.RowDefinitions>
<Viewbox VerticalAlignment="Stretch" Height="Auto">
<!-- The textblock and its contents are
stretched to fill its parent -->
<TextBlock Text="Sphinx" />
</Viewbox>
<Viewbox Grid.Row="2" VerticalAlignment="Stretch" Height="Auto">
<!-- The textblock and its contents are
stretched to fill its parent -->
<TextBlock Text="Sphinx2" />
</Viewbox>
or you can bind the FontSize to a Container element like;
<Grid x:Name="MyText" Height="120">
<TextBlock FontSize="{Binding ElementName=MyText, Path=Height}" Text="Sphinx" />
</Grid>
They might present the effect you're after?