Here is my XAML code:
<TextBox HorizontalAlignment="Left" Height="24" Margin="168,352,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="280">
<TextBox.Resources>
<sys:Double x:Key="fixedValue">2</sys:Double>
</TextBox.Resources>
<TextBox.Text>
<MultiBinding Converter="{StaticResource DoubleConverter}">
<Binding Path="RM.SpecificGravity"/>
<Binding Source="{StaticResource fixedValue}"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
This is giving me this error:
Two-way binding requires Path or XPath.
What is causing this and how can I fix it?
As the error message says, you need to set the Path of the Binding. For binding directly to the Source object, you can set Path=".":
<Binding Path="." Source="{StaticResource fixedValue}"/>
That said, your MultiBinding might be replaced by a normal Binding, where the fixedValue is passed as ConverterParameter
<TextBox Text="{Binding Path=RM.SpecificGravity,
Converter={StaticResource DoubleConverter},
ConverterParameter=2}" />
with a value-converter like this:
public class DoubleConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var p = double.Parse(parameter.ToString());
...
}
...
}
Related
I am developing a chat application in Xamarin Forms and I am trying to add conditional formatting depending on whether it is an incoming or outgoing message.
This is my XAML:
<Frame
Margin="1"
Padding="0"
x:Name="FrameRef"
x:DataType="model:ChatMessage">
<Frame
CornerRadius="10"
Padding="7"
BackgroundColor="LightBlue"
HasShadow="false"
Margin="10,10,80,0">
<Frame.Triggers>
<DataTrigger
TargetType="Frame"
Binding="{Binding Source={x:Reference FrameRef}, Path=x:DataType.From}" Value="+1456456456">
<Setter Property="BackgroundColor" Value="Yellow"/>
</DataTrigger>
</Frame.Triggers>
When I use Path="Margin" and Value="1" it works.
I am now trying to make it work with the Path being x:DataType="model:ChatMessage" and checking the 'from'-field (indicating if the message was incoming or outgoing).
I'm not sure the Data Trigger is quite right for this application, since you're really depending on a data type and not really the content per se of another field. From the documentation:
The DataTrigger class is suitable for checking values on other controls, as well as any property on the control to which it has been added.
What you probably want instead is a Value Converter that handles locating a StaticResource and applying a style for you based on the message type. Full Microsoft Documentation here.
On your XAML element, you'd do something like this:
<Frame Style="{Binding foo, Converter={StaticResource FooToStyleConverter}}"/>
Your converter would work something like this:
public class FooToStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var someValue = (DataTye)value; // Convert 'object' to whatever type you are expecting
// evaluate the converted value
if (someValue.From != null && someValue.From == Enum.SomeoneElse)
return (Style)App.Current.Resources["StyleReceived"]; // return the desired style indicating the message is from someone else
return (Style)App.Current.Resources["StyleSent"]; // return a style indicating the message is from the sender
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Usually unused, but inverse the above logic if needed
throw new NotImplementedException();
}
}
Lastly, set up your converter as a Static Resource in App.xaml (or as a local resource on the page) so your page can properly reference it
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos">
<ContentPage.Resources>
<ResourceDictionary>
<local:FooToStyleConverter x:Key="FooToStyleConverter" />
....
Microsoft uses a specific symbol for informationial purposes it is a circle with the letter i inside Image of the Symbol. I looked at every resource about the Segoe MDL2 Assets Font but did not find that symbol. Does anyone know if this symbol is part of the font or is it just another image?
The symbol code point is E946.
The following WPF code snippet creates an IEnumerable<int> that contains all symbol code points in Segoe MDL2 Assets.
var typeface = new Typeface(
new FontFamily("Segoe MDL2 Assets"),
FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
GlyphTypeface glyphTypeface;
typeface.TryGetGlyphTypeface(out glyphTypeface);
var codePoints = glyphTypeface.CharacterToGlyphMap.Keys.Where(c => c > 0x20);
You can easily visualize this collection by setting DataContext = codePoints and writing an ItemsControls like this:
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2" VerticalAlignment="Center"
Text="{Binding StringFormat={}{0:X4}}"/>
<TextBlock
Margin="2" FontFamily="Segoe MDL2 Assets" FontSize="24"
Text="{Binding Converter={StaticResource CodePointConverter}}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
with this CodePointConverter class:
public class CodePointConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new string((char)(int)value, 1);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
How do I use a template dynamically based on a Bound string (This complains that the Static Resource must be a string, even thought the Binding will return a string):
<ContentControl ContentTemplate="{StaticResource {Binding Template}}">
<ContentPresenter />
</ContentControl>
So Template is actually just a string that will return which template to use?
I'm trying to use a DataTriggerBehavior from the Behaviors SDK. But it doesn't seem to work with enums... or else I'm doing something wrong.
You can assume that the DataContext for these examples is something like this (INotifyPropertyChanged is implemented, but I'm not going to show it here):
public class MyDataClass
{
public MyEnum ItemCommand { get; set; }
public string ItemCommandString { get; set; }
}
public enum MyEnum
{
EnumValue1
}
_Button.DataContext = new MyDataClass() { ItemCommand = MyEnum.EnumValue1,
ItemCommandString = "EnumValue1" };
Here is the code that doesn't work (trying to specify an enum value and check against the ItemCommand enum property):
<ToggleButton x:Name="_Button">
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding ItemCommand}"
Value="EnumValue1">
<Core:ChangePropertyAction PropertyName="Command"
TargetObject="{Binding ElementName=_Button}"
Value="{x:Null}">
</Core:ChangePropertyAction>
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</ToggleButton>
and this code (checking against an enum resource) also does not work:
<UserControl.Resources>
<local:MyEnum x:Key="_MyEnumValue">EnumValue1</local:MyEnum>
</UserControl.Resources>
<ToggleButton x:Name="_Button">
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding ItemCommand}"
Value="{StaticResource _MyEnumValue}">
<Core:ChangePropertyAction PropertyName="Command"
TargetObject="{Binding ElementName=_Button}"
Value="{x:Null}">
</Core:ChangePropertyAction>
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</ToggleButton>
whereas this code (checking against a string) does work:
<ToggleButton x:Name="_Button">
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding ItemCommandString}"
Value="EnumValue1">
<Core:ChangePropertyAction PropertyName="Command"
TargetObject="{Binding ElementName=_Button}"
Value="{x:Null}">
</Core:ChangePropertyAction>
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</ToggleButton>
What is the correct way to specify the enum value in the DataTriggerBehavior Value property so that this will work?
you can write a Converter:
public class MyEnumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
MyEnum myEnumValue = (MyEnum)value;
return myEnumValue.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
And use it in XAML:
<ToggleButton x:Name="_Button">
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding ItemCommand, Converter={StaticResource MyEnumConverter}}"
Value="EnumValue1">
<Core:ChangePropertyAction PropertyName="Command"
TargetObject="{Binding ElementName=_Button}"
Value="{x:Null}">
</Core:ChangePropertyAction>
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</ToggleButton>
Or bind direct to the string as in your sample. Unfortunately DataTriggerBehavior in WinRT is worse that DataTrigger in Windows Phone 8
I was investigating this issue and narrowed the problem down to TypeConverterHelper class.
TypeConverterHelper source
Apparently it doesn’t account for enum types and falls back to some logic which recreates the xaml string for the enum. Parses it as ContentControl and passes back its content. Unfortunately during this step it loses the enum type information and subsequent type casting is not valid.
If you are working with sources and not just NuGet package you can fix it yourself. Just add another overload of Convert method to TypeConverterHelper:
public static Object Convert(string value, Type destinationType)
{
var typeInfo = destinationType.GetTypeInfo();
if (typeInfo.IsEnum)
return Enum.Parse(destinationType, value);
return Convert(value, destinationType.FullName);
}
And of course change the call in DataTriggerBehavior Compare method
from:
rightOperand = TypeConverterHelper.Convert(rightOperand.ToString(), leftOperand.GetType().FullName);
to:
rightOperand = TypeConverterHelper.Convert(rightOperand.ToString(), leftOperand.GetType());
I have a property in my ViewModel called RelativeHeight, which is a double ranging from 0 to 1.
In my View, I have an horizontal line whose width is the same of its container (via Element Binding), but I want it to have its vertical position relative to the size of the container.
For example, if RelativeHeight is 0.3, and the container's ActualHeight is 200, then Line.X1 and Line.X2 would be 60 each.
Following code is what I got, but don't know how to use (or even if I should use in the first place) some IValueConverter because usually I can't get properties from the view whan calling the Convert method...
<Line Stroke="Red" Opacity="0.5" StrokeThickness="5"
X1="0" X2="{Binding ActualWidth, ElementName=Graphs}"
Y1="{Binding RelativeHeight, Converter=MaybeSomeConversion}"
Y2="{Binding RelativeHeight, Converter=MaybeSomeConversion}" />
Got it with IMultiValueConverter, like this (variable names in portuguese):
... (resource dictionary)
<views:ConversorNível x:Key="conversorNivel"/>
....
<Line x:Name="line" Stroke="Red" Opacity="0.6" Grid.ColumnSpan="5" StrokeThickness="2"
X1="0" X2="{Binding ActualWidth, ElementName=Gráficos}"
Y2="{Binding Y1, ElementName=line}" >
<Line.Y1>
<MultiBinding Converter="{StaticResource conversorNivel}">
<Binding Path="NívelSelecionado" />
<Binding ElementName="Gráficos" Path="ActualHeight" />
</MultiBinding>
</Line.Y1>
</Line>
and in codebehind:
public class ConversorNível : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (double)values[0] * (double)values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}