I have a DataTemplate:
<DataTemplate
DataType="{x:Type local:ConnectionViewModel}"
>
<!-- The connection is represented by a curved arrow. -->
<ca:CurvedArrow
StrokeThickness="2"
Points="{Binding Points}"
Fill="{StaticResource connectionBrush}"
Stroke="{StaticResource connectionBrush}"
/>
</DataTemplate>
This represent all the connector i have in my view.
What I want to do is to set a different fill and stroke for specific specific connectors from the viewmodel.
How can I achive that?
You should be avoiding any UI objects in viewmodel.
For the mentioned use case, you can make use of Converters and continue to only have business object level information in your viewmodel.
For instance, you Connection class can hold a property of enum : ConnectorType {Arrow,Circle,Rectangle} and then you can write a Converter which converts enum type to desired color brush. Sample code below:
//Inside Resources. local=namespace where you have this converter
<local:ConnectorType2BrushConverter x:Key="ConnectorType2BrushConverter" />
....
<ca:CurvedArrow
StrokeThickness="2"
Points="{Binding Points}"
Fill="{Binding Path=ConnectorType, Converter={StaticResource ResourceKey=ConnectorType2BrushConverter}"
/>
....
public class ConnectorType2BrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var connectorType = (ConnectorType)value;
if (connectorType == ConnectorType.Arrow)
{
return new SolidColorBrush(Color.FromRgb(1, 1, 1));
}
else .....
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
What I want it to bind TextBoxes to my dictionaries values.
I could find some posts about it already but :
One means having my dictionary as context :
XML :
<TextBox x:Name="FirstLine" Text="{Binding Path=[FirstLine]}"/>
XAML :
public ImportProfilesOptions()
{
InitializeComponent();
contexte = new ViewModelImportProfilesOptions();
DataContext = contexte.ParamsData;
}
The other one using Templates :
<ItemsControl x:Name="dictionaryItemsControl" ItemsSource="{Binding dictionary}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But I would like to use it, without using Templates (I need to add in labels some translates I take from properties), and without setting my dictionary as context. Something like that :
XML
<TextBox x:Name="FirstLine" Text="{Binding Path=ParamsDate[FirstLine]}" />
XAML
contexte = new ViewModelImportProfilesOptions();
DataContext = contexte;
But then, binding is not working anymore.
You can't do this out of the box, though you could create your own converter I guess:
public class SomeFunkyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is Dictionary<string,string> dictionary))
return null;
if (!(parameter is string key))
return null;
return !dictionary.TryGetValue(key, out var result) ? null : result;
}
// ill leave this up to you
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
Usage
<TextBlock Text="{Binding Path=ParamsDate,
ElementName=TextBox,
Converter={StaticResource SomeFunkyConverter},
ConverterParameter='Bob'}"/>
Note : Completely untested
My localized resource string, named TextResource has the value: Text: {0}. Where {0} is the placeholder for String.Format.
My user control has a DependecyProperty called Count.
I would like to bind Count to the text of a text box but also apply the localized string. So that the content of the text block is Text: 5 (assuming the value of Count is 5)
I managed to figure out how to bind the localized string
<TextBlock Text="{Binding Path=LocalizedResources.TextResource, Source={StaticResource LocalizedStrings}}" />
or the property value
<TextBlock Text="{Binding Path=Count}" />
but not both simultaneous.
How can I do that in XAML?
PS: One option would be to add two text blocks instead of one but I am not sure if that is a good practice.
You have three options here.
First option: Modify your view model to expose your formatted string and bind to that.
public string CountFormatted {
get {
return String.Format(AppResources.TextResource, Count);
}
}
<TextBlock Text="{Binding Path=CountFormatted}" />
Second option: Make a converter MyCountConverter
public class MyCountConverter: IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value == null)
return value;
return String.Format(culture, AppResources.TextResource, value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
<phone:PhoneApplicationPage.Resources>
<local:MyCountConverter x:Key="MyCountConverter"/>
</phone:PhoneApplicationPage.Resources>
...
<TextBlock Text="{Binding Count, Converter={StaticResource MyCountConverter}}"/>
Third option: Use bind-able converter parameter so that you can make a general StringFormat converter where you can actually bind the converter parameter. This is not supported out of the box in windows phone but is still doable. Check this link on how it can be done.
However, unless you are using resources to support multiple languages then it's much easier to just pass your format as a plain string to a converter.
<TextBlock Text="{Binding Count,
Converter={StaticResource StringFormatConverter},
ConverterParameter='Text: {0}'}" />
You'll have to make a StringFormatConverter converter that uses the parameter in this case.
Edit:
Regarding third option, you can use the IMultiValueConverter in the link above to achieve what you want. You can add the following converter:
public class StringFormatConverter: IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
var param = values[0].ToString();
var format = values[1].ToString();
return String.Format(culture, format, param);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
<TextBlock Text="{Binding ElementName=MultiBinder, Path=Output}" />
<binding:MultiBinding x:Name="MultiBinder" Converter="{StaticResource StringFormatConverter}"
NumberOfInputs="2"
Input1="{Binding Path=Count, Mode=TwoWay}"
Input2="{Binding Path=LocalizedResources.TextResource, Source={StaticResource LocalizedStrings}}" />
I don't know if it's worth the effort though.
I would like to dynamic do the columnSpan on the userControl. I created the converter class, but it didn’t work. Would you show me how to do it correctly? Thanks.
The code on my UserControl:
<TextBlock x:Name="txtSumary" Grid.Row="0" Grid.Column="1" Text="{Binding summary}"
TextWrapping="Wrap" Style="{StaticResource PhoneTextAccentStyle}" Grid.ColumnSpan="{Binding isSpan, Converter={StaticResource ColumSpanConverter}}" />
It is reference on the UserControl.Resources
<local:VisibilityConverter x:Key="ColumSpanConverter"/>
There is the Converter Class:
public class ColumSpanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isSpan = (bool)value;
return isSpan ? 2 : 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
The converter is referencing the wrong converter:
<local:VisibilityConverter x:Key="ColumSpanConverter"/>
Should be:
<local:ColumSpanConverter x:Key="ColumSpanConverter" />
I need to make some complex binding in XAML. I have a DependencyProperty typeof(double); let's name it SomeProperty. Somewhere in XAML code of my control, I need to use the whole SomeProperty value, somewhere only a half, somewhere SomeProperty/3, and so on.
How can I do something like:
<SomeControl Value="{Binding ElementName=MyControl, Path=SomeProperty} / 3"/>
:)
Looking forward.
Use a division ValueConverter:
public class DivisionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int divideBy = int.Parse(parameter as string);
double input = (double)value;
return input / divideBy;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
<!-- Created as resource -->
<local:DivisionConverter x:Key="DivisionConverter"/>
<!-- Usage Example -->
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=1}"/>
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=2}"/>
<TextBlock Text="{Binding SomeProperty, Converter={StaticResource DivisionConverter}, ConverterParameter=3}"/>
I have a property of datattype enum : like
public BreakLevel Level
{
get { return level; }
set { level = value; }
}
And enum defined :
public enum BreakLevel
{
Warning, Fatal
}
I want bind the neum property to the visibility of my border , somewhat like this:
Visibility="{Binding BreakLevel.Fatal}"
so is it possible?
<Border CornerRadius="4" BorderThickness="1" BorderBrush="#DAE0E5"
Visibility="{Binding DataContext.IsError, Converter={StaticResource BoolToVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}" >
Scott has a good answer to the actual question, however its always a good idea to ask yourself "How might I need code like this in the future? How can I avoid creating yet another class and instead re-use what I have already got?".
Here is a more general variation of Scott's solution:-
public class EnumToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (Enum.GetName(value.GetType(), value).Equals(parameter))
return Visibility.Visible;
else
return Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Xaml:-
<TopLevelWindowOrControl.Resources>
<local:EnumToVisibilityConverter x:Key="EnumToVisibilityConverter" />
</TopLevelWindowOrControl.Resources>
<Border Visibility="{Binding Path=BreakLvlProperty, Converter={StaticResource EnumToVisibilityConverter}, ConverterParameter=Fatal" />
In this approach we can converter any enum to a Visibility value by using the ConverterParameter to specify the enum value (in string form) that constitute the "Visible" state.
Its tempting to take this further to allow more than one enum value to be equated to "Visible". However currently the code isn't much more complicated than Scott's more specific implementation. Hence this enhancement should be left until needed.
I think you can just create a BreakLevelToVisibilityConverter and bind just like the example you provided.
I'm assuming that the DataContext of your border is set to an instance of a class that has a property of type 'BreakLevel' (we'll call this property 'BreakLvlProperty').
The code below will then show the border if the value of BreakLvlProperty is BreakLevel.Fatal
Converter:
public class BreakLevelToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((BreakLevel)value == BreakLevel.Fatal)
return Visibility.Visible;
else
return Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
XAML:
<TopLevelWindowOrControl.Resources>
<local:BreakLevelToVisibilityConverter x:Key="BreakLevelToVisibilityConverter" />
</TopLevelWindowOrControl.Resources>
<Border Visibility="{Binding Path=BreakLvlProperty, Converter={StaticResource BreakLevelToVisibilityConverter}" />
public class EnumToVisibilityConvertor : IValueConverter
{
private bool chk;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((value != null) && (value is BreakLevel) && (targetType == typeof(Visibility)))
{
chk = ((((BreakLevel) value) == (BreakLevel) Enum.Parse(typeof (BreakLevel), parameter.ToString(), true)));
return (chk==true) ? Visibility.Visible : Visibility.Collapsed;
}
throw new InvalidOperationException("Invalid converter usage.");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
<Border CornerRadius="4" BorderThickness="1" BorderBrush="#DAE0E5"
Visibility="{Binding Path=Level, Converter={StaticResource enumToVisibilityConvertor},ConverterParameter=Fatal}" >
I know it's a bit old question but all of you focused on converters and there's much better (in my opinion) way of doing it without involving code-behind:
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<Grid>
<!-- Here is the border which will be shown if Object.Level has value Fatal -->
<Border x:Name="PART_Border"
Visibility="Collapsed"
BorderThickness="3" BorderBrush="Red"
CornerRadius="4">
</Border>
<TextBlock>Interiors of the border</TextBlock>
</Grid>
<ControlTemplate.Triggers>
<!-- This is the code which turns on border's visibility -->
<DataTrigger Binding="{Binding Level}" Value="{x:Static local:BreakLevel.Fatal}">
<Setter TargetName="PART_Border" Property="Visibility" Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
I assumed that in DataContext resists an object which has a property Level which is of your BreakLevel type.