xamarin form: text binding enum status converter issue - wpf

I create a button, its content binding a status, I initialize the status, but the button content can't display corresponding text, in debug mode, I found it seems execute into a loop, after get the status, execute Convert function, then directly skip to the ConvertBack(), and back to Convert() again.
my code in xaml file is :
<ListView x:Name="myListView" ItemsSource="{Binding players}" HorizontalOptions="FillAndExpand" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
...
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Text="{Binding Status, Converter={StaticResource StatusToTextConverter}, Mode=TwoWay}" HorizontalOptions="CenterAndExpand"/>
<Button Grid.Column="1" Text="{Binding Status, Converter={StaticResource StatusToTextExConverter}, Mode=TwoWay}" HorizontalOptions="CenterAndExpand"/>
...
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
and my converter is:
public class StatusToTextConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var status = (EStatus)value;
if (status == EStatus.YES)
{
return "V";
}
else
{
return "";
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var text = (string)value;
if (text == "V")
return EStatus.YES;
else
return EStatus.NO;
}
#endregion
}

It's probably the binding mode of TwoWay that is causing the loop.
You need a way to break it.
In the view model property that it is bound to, check if the new value is the same as the old one and only set property changed if different.

Remove the implementation from Convert Back

Related

Bind to a System.Windows Enum from xaml

I am trying to bind a ComboBox ItemsSource to the TextWrapping enum within the System.Windows namespace. The end result would be a drop down where the user can select which type of text wrapping to apply for a given object within my application. Everything works fine when I bind to a custom enum, but I can't figure out what path/source I need to use to bind to an enum within the System.Windows namespace. How can I access this namespace through data binding?
<DataTemplate
DataType="{x:Type MyObjectWrapper}"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Text Wrapping" VerticalAlignment="Center" Margin="5,5,0,5"/>
<ComboBox
ItemsSource="{Binding Source={???}, Converter={local:MyEnumConverter}}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path = TextWrapping}"
VerticalAlignment="Center"
Margin="5"
/>
</StackPanel>
</DataTemplate>
Update: My enum converter just needs the enum class passed in the xaml, which looks like this for custom enums:
<ComboBox
ItemsSource="{Binding Path=MyCreatedEnum, Converter={local:MyEnumConverter}}"
SelectedValuePath="Value"
DisplayMemberPath="Description"
SelectedValue="{Binding Path = TextWrapping}"
VerticalAlignment="Center"
Margin="5"
/>
Found this over the web under this link.
http://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/
Here is the modified code to suite your requirement.
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<local:EnumToListConverter x:Key="enumToListConv" />
</Grid.Resources>
<ComboBox
Margin="5"
VerticalAlignment="Center"
ItemsSource="{Binding Source={local:EnumBindingSource {x:Type sysWin1:TextWrapping}}, Converter={StaticResource enumToListConv}}"
SelectedValuePath="Value" />
</Grid>
public class EnumBindingSourceExtension : MarkupExtension
{
private Type _enumType;
public Type EnumType
{
get { return this._enumType; }
set
{
if (value != this._enumType)
{
if (null != value)
{
Type enumType = Nullable.GetUnderlyingType(value) ?? value;
if (!enumType.IsEnum)
throw new ArgumentException("Type must be for an Enum.");
}
this._enumType = value;
}
}
}
public EnumBindingSourceExtension() { }
public EnumBindingSourceExtension(Type enumType)
{
this.EnumType = enumType;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (null == this._enumType)
throw new InvalidOperationException("The EnumType must be specified.");
Type actualEnumType = Nullable.GetUnderlyingType(this._enumType) ?? this._enumType;
return actualEnumType;
}
}
public class EnumToListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var items = Enum.GetValues((Type)value);
return items;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF MarkupExtension binding error

refers to implementing a multilanguage interface now I have a problem. I have a grid where its ItemSource it's a Dictionary<string, string>: I can't give the key value as parameter of the markup extension because I have the following exception
A 'Binding' cannot be set on the 'Value1' property of type 'TranslateMarkupExtension'.
A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
This is the xaml where I use the markup extension:
<ItemsControl ItemsSource="{Binding MyDictionary}" Grid.Row="0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Content="{TranslateMarkup Value1 = {Binding Path = Key}}" Grid.Column="0" />
<TextBox Tag="{Binding Key, Mode=OneWay}" Text="{Binding Value, Mode=OneWay}" Width="200" Height="46" VerticalContentAlignment="Center" Grid.Column="1" LostFocus="TextBox_OnLostFocus" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I can't find any workaround..I need the dictionary as item source because the label will show the key and the textbox the corresponding value..I tried also replacing the dictionary with a list of object but the problem still remains.
Does anyone have a good hint? Thanks in advance.
As suggested by Ed Plunkett, the solution is the ValueConverter.
So the label is know this:
<Label Content="{Binding Key, Converter={StaticResource MultiLangConverter}}" Grid.Column="0" />
where my ValueConverter is the following:
public class StringToMultiLangConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var stringValue = (string) value;
if (string.IsNullOrEmpty(stringValue))
return string.Empty;
string multiLang = LangTranslator.GetString(stringValue);
return multiLang ?? value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
Thanks for the help!

Enum binding with converter

I have Languages enum. And My Model.Translations is representing different languages translations:
public ObservableCollection<LanguageValue> Translations { get; set; }
public class LanguageValue
{
public Language Key { get; set; }
public string Value { get; set; }
}
I want my view to have label - textbox list for each item in Translations.
But in Label I want to have something like "Caption ({0})", where parameter is Language name (enum to string representation). This text itself is coming from Resources.
Something like:
<ItemsControl ItemsSource="{Binding Path=Translations}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="5,2,5,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*"/>
<ColumnDefinition Width="70*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="ItemLabel" VerticalAlignment="Center"
Text="{Binding Path=Key, Converter=languageConverter, ConverterParameter={x:Static res:Resources.lblCaption}}" />
<TextBox Grid.Column="1" x:Name="ItemText" VerticalAlignment="Center"
Text="{Binding Path=Value, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
My LanguageConverter:
[ValueConversion(typeof(Language), typeof(string))]
public class LanguageToDisplayConverter : IValueConverter
{
public object Convert(object value, Type t, object parameter, CultureInfo culture)
{
return string.Format(parameter.ToString(), ((Language)value).ToString());
}
public object ConvertBack(object value, Type t, object parameter, CultureInfo culture)
{
//I guess I don't need this anyway?
return null;
}
}
But I've got error:
The TypeConverter for "IValueConverter" does not support converting from a string.
What is wrong?
you should declare your converter as a resource (in a Window, or app-wide in App.xaml):
<Window.Resources>
<views:LanguageToDisplayConverter x:Key="languageConverter"/>
</Window.Resources>
and use accordingly:
Text="{Binding Path=Key, Converter={StaticResource languageConverter}, ...
the error "The TypeConverter for "IValueConverter" does not support converting from a string" indicates that wpf didn't recognize string "languageConverter" as a converter
the same effect can be also achieved by using StringFormat property of Binding, without converter:
Text="{Binding Path=Key, StringFormat={x:Static res:Resources.lblCaption}}"

Partially binding to a list

I have a collection of a few thousand elements and I want to display only a subset of them. Is there a way to bind the collection to a view such that only certain elements, e.g. those with property "Show == true", are displayed? If so, would it still create thousands of UI elements? Or do I have to create a new list of the to-be-shown elements?
Check CollectionViewSource Class and CollectionViewSource.Filter Event
View(partial example):
<Grid>
<Grid.DataContext>
<wpfCalc:StudentList/>
</Grid.DataContext>
<Grid.Resources>
<CollectionViewSource
Source="{Binding Students,Mode=OneWay}" x:Key="StudentsCollViewSource"
Filter="StudentsCollViewSource_OnFilter"/>
</Grid.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource StudentsCollViewSource}}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name,Mode=OneTime}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
And code behind filter implementation:
private void StudentsCollViewSource_OnFilter(object sender, FilterEventArgs e)
{
var s = e.Item as Student;
e.Accepted = s != null && !string.IsNullOrWhiteSpace(s.Name);
}
There are two methods. First one is to use a converter.
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="converter"/>
</Window.Resources>
<StackPanel>
<ListView x:Name="listView" ItemsSource="{Binding List}">
<ListView.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding Name}" Visibility="{Binding IsActive, Converter={StaticResource converter}}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
//Code Behind
public class BoolToVisibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return false; // not needed
}
#endregion
}
Second method is to use linq
List<MyData> list
{
get
{
if(list!=null)
return list.where( p => p.IsActive == true );
return null;
}
set
{
if(list!=value)
list = value;
}
}

WPF binding IValueConverter and the width of another control

The following code worked fine. There was an error elsewhere in the code. Still, the advice given is good.
I am trying to bind the Width of a TextBox to a percentage of the Width of a parent control. I know I can accomplish something similar by simply setting a Margin, but I was wondering why this doesn't work.
First, I set a reference to an IValueConverter in the resources collection of my user control:
<UserControl.Resources>
<local:TextBoxWidthConverter x:Key="txtWidthConv" />
</UserControl.Resources>
In the main xaml, I have the following:
<StackPanel Name="parentPanel" Width="300">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Hidden" Name="scroller" Width="{Binding Width,
ElementName=parentPanel, Converter={StaticResource txtWidthConv}}">
<StackPanel Orientation="Horizontal">
<TextBox></TextBox>
</StackPanel>
</ScrollViewer>
</StackPanel>
The ivalueconverter looks like this:
public class TextBoxWidthConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double result = (double)value;
if (!Double.IsNaN(result))
{
result = result * .25;
}
else
{
result = 100D;
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException("Not implemented.");
}
#endregion
}
Setting the width property does nothing here, let alone setting the IValueConverter. I would expect the ScrollViewer to be 1/4 the width of the parent StackPanel.
Set the ScrollViewer's HorizontalAlignment to something other than Stretch.
Also, you should bind to the ActualWidth property.
Let the layout system work for you instead of fighting it. Grid will automatically handle relative sizing:
<StackPanel Name="parentPanel" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Hidden" Name="scroller">
<StackPanel Orientation="Horizontal">
<TextBox></TextBox>
</StackPanel>
</ScrollViewer>
</Grid>
</StackPanel>

Resources