I am pulling data from a previous page which was a selected item in a listbox from a wcf service.
Ther error I am having is that the textblock is not reading the formatting in my in my data.
this is the code that brings the data in from the previous page
private void LoadPlayer()
{
FrameworkElement root1 = Application.Current.RootVisual as FrameworkElement;
var currentPlayer = root1.DataContext as PlayerProfile;
_SelectedPlayer = currentPlayer;
}
this is the xaml
<TextBlock Height="Auto" TextWrapping="Wrap" Name="Blurb" Text="{Binding Bio}" xml:space="preserve" />
specifically I am trying to get the \r\n to work in my display as a linebreak.
See the answer here:
Newline in string attribute
In your case what you need to do write a Converter (something that implements IValueConverter) that turns the string data that contains the \r\n into encoded entities i.e.
and
. Then just use that converter on your Binding.
public class EncodeCRLFConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string stringtoconvert = value as string;
if (input != null))
{
// Note there are different ways to do the replacement, this is
// just a very simplistic method.
stringtoconvert = stringtoconvert.Replace( "\r", "
" );
stringtoconvert = stringtoconvert.Replace( "\n", "
" );
return stringtoconvert;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
Create an instance of your converter somewhere...e.g. typically in .Resources....(in this example I've just used Window because I don't know what your TextBlock is inside).
<Window.Resources>
<EncodeCRLFConverter x:Key="strconv"/>
<Window.Resources>
<TextBlock Height="Auto" TextWrapping="Wrap" Name="Blurb" Text="{Binding Bio, Converter={StaticResource strconv}}" />
Related
I have label that is bound to my view model. Sometimes the text value of the label is only a few characters, but sometimes it is more than 20 characters.
How to limit maximum length of label text?
Check out the LineBreakMode property:
https://learn.microsoft.com/en-us/dotnet/api/xamarin.forms.label.linebreakmode?view=xamarin-forms
You can set your Label's LineBreakMode to TailTruncation to make the Label truncate any characters that don't fit in its allotted space and replace them with ellipses.
<Label Text="{Binding LabelText}" LineBreakMode="TailTruncation" />
Then you just need to limit the allotted width of the Label to an acceptable size. How you do that will depend on your specific scenario.
You can use a converter:
public class LabelMaxLengthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string labelText = value as string;
if (labelText == null)
return value;
const int maxLength = 20;
if (labelText.Length > maxLength)
return labelText.Substring(0, maxLength);
else
return labelText;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And in your xaml first define that converter in pages's resouurces:
<ContentPage.Resources>
<converters:LabelMaxLengthConverter x:Key="LabelMaxLengthConverter" />
</ContentPage.Resources>
And finally in your label apply the converter to binding:
<Label Text="{Binding LabelText, Converter={StaticResource LabelMaxLengthConverter}}" />
I a have typical label that I want to control its visibility by a property that is null/empty or not.
I've put breakpoints and also log and it seems that return value is true but still it does not show the element. When I scroll my listview then they are visible but sometimes still not.. There are several items, sometimes some of them are visible sometimes not.. it is changable..
here my converter
public class TestBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var result= value != null && !value.ToString().Equals("");
Console.WriteLine("Result: " +result);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and the property
public string LocalizedReadoutDescription
{
get
{
Console.WriteLine("Description: " + dataItem.Description);
string localizedDescription = null;
if (!string.IsNullOrEmpty(this.dataItem.Description))
{
string[] descriptionKeyParts = this.dataItem.Description.Split(';');
localizedDescription = descriptionKeyParts[0];
if (!string.IsNullOrEmpty(localizedDescription))
{
localizedDescription =
this.getLocalizedString(Constants.Localization.LogicalItemDescriptionFmt,
localizedDescription);
}
}
return localizedDescription;
}
}
and Xaml Code
<ContentView.Resources>
<converters:TestBooleanConverter x:Key="nullToBoolConverter"/>
.....
</ContentView.Resources>
.....
<StackLayout Orientation="Horizontal" Margin="4,2" Grid.Column="1" VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand" Spacing="0" BackgroundColor="White">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="Help_Tapped" CommandParameter="{Binding}"/>
</StackLayout.GestureRecognizers>
<Label x:Name="HelpLabel" Style="{StaticResource InfoIconLabel}" Text="{x:Static resx:UI.Icon_Info}" Margin="0"
HorizontalOptions="EndAndExpand" VerticalOptions="Start" IsVisible="{Binding LocalizedReadoutDescription,
Converter={StaticResource nullToBoolConverter}}" FontSize="Micro" LineBreakMode="NoWrap"/>
</StackLayout>
I feel that, even it returns true, but it uses the previous rows value. but only this part does not update. label names, values are updated, but only some items' visibility is not updating..
where is my mistake?
Update:
I've created an event for property change of the label that I want to control its visibility. I can see that, the IsVisible is always true but on the GUI, only one item is visible.. but when I scroll, several are visible, and when I scroll more then all items are visible as it must be
private void HelpLabel_OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals("IsVisible"))
{
// I check => ((Xamarin.Forms.Label)sender).IsVisible
//always true
}
}
I have found the problem. It was not regarding cache or informing UI element. Somehow it was regarding the item's height. I've gave a height and heightrequest value, and then it works always.. weird..
In the ListView, I need to show some images and text, only display the text when the Image.Source is empty. How to do?
<ListView ItemsSource="{x:Bind ViewModel.News}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="home:NormalNews">
<StackPanel>
<TextBlock Text="{x:Bind Title}"/>
<Image Source="{x:Bind Thumbnail}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel Data Source like:
News.Add(new NormalNews{ Title = "title1", Thumbnail = "http://a.com/test.jpg" });
News.Add(new NormalNews{ Title = "title2", Thumbnail = "" });
When I tried to run this page, it stopped running.
You can handle this using xaml converters.
Add a converter and define a key for it in your page.
In XAML page
<converter:ImageUriConverter x:Key="ImageUriConvert"/>
In your list
<Image Source="{Binding Thumbnail,Converter={StaticResource ImageUriConvert}}"/>
Converter class code
class ImageUriConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value.ToString() == string.Empty)
{
return new BitmapImage();
}
return new BitmapImage(new Uri(value.ToString()));
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
While you are populating the ViewModel object, do this:
ViewModel.News.Add(new NormalNews{
Thumbnail=(the Thumbnail source which you will be using),
Title=(the Thumbnail source which you will be using)==""?"Your Title":""});
This will set your Title to the specified string when your Thumbnail source is empty.
Hope this helps.
For now, I have something like that (2 columns with dropboxes containing values independent from each other):
<xcdg:DataGridControl.Columns>
<xcdg:Column Title="A"
FieldName="A"
CellContentTemplate="{StaticResource ADT}"
GroupValueTemplate="{StaticResource ADT}"
Converter="{StaticResource AConverter}"
CellEditor="{StaticResource AEditor}"/>
<xcdg:Column Title="B"
FieldName="B"
CellContentTemplate="{StaticResource BDT}"
GroupValueTemplate="{StaticResource BDT}"
Converter="{StaticResource BConverter}"
CellEditor="{StaticResource BEditor}"/>
</xcdg:DataGridControl.Columns>
And I would like the B column to be a dropbox containing values depending on the value selected in the first column.
I don't know how to achieve that. I read about Binding.RelativeSource but I think it is not at all what I should use.
Thanks
I can think of two ways to do that, and since you didn't provide your exact case, i will provide a simple scenario and build my answer base on it,
let say you have a DataGrid with two editable columns (A and B), in the edit mode you can choose the A value from a combobox list, and then the B combobox will be filtered to show only the items whom their value starts with the A value for example, if A="aa", B should be {"aaaa","aabb"}, to implement that you need first a Model that represent the DataGrid Items
public class GridItem
{
public String A { get; set; }
public String B { get; set; }
}
In your codebehind / ViewModel define those properties (the DataGrid , and the comboboxes ItemSource Collections) :
private ObservableCollection<GridItem> _gridItemsCollection = new ObservableCollection<GridItem>()
{
new GridItem()
{
A="aa",
B="bbbb"
}
};
public ObservableCollection<GridItem> GridItemsCollection
{
get
{
return _gridItemsCollection;
}
set
{
if (_gridItemsCollection == value)
{
return;
}
_gridItemsCollection = value;
OnPropertyChanged();
}
}
//for the first Combobox
private ObservableCollection<String> _aCollection = new ObservableCollection<String>()
{
"aa",
"bb"
};
public ObservableCollection<String> ACollection
{
get
{
return _aCollection;
}
set
{
if (_aCollection == value)
{
return;
}
_aCollection = value;
OnPropertyChanged();
}
}
//for the second Combobox
private ObservableCollection<String> _bCollection ;
public ObservableCollection<String> BCollection
{
get
{
return _bCollection;
}
set
{
if (_bCollection == value)
{
return;
}
_bCollection = value;
OnPropertyChanged();
}
}
Define a full B collection from which your B combobox's itemsource will be populated
ObservableCollection<String> MainBCollection = new ObservableCollection<String>()
{
"aaaa",
"aabb",
"bbaa",
"bbbb"
};
Finally the population of the B combobox will be based on the selected item in the A combobox using this property,
private String _selectedAItem;
public String SelectedAItem
{
get
{
return _selectedAItem;
}
set
{
if (_selectedAItem == value)
{
return;
}
_selectedAItem = value;
OnPropertyChanged();
var returnedCollection = new ObservableCollection<String>();
foreach (var val in MainBCollection)
{
if (val.StartsWith(_selectedAItem))
{
returnedCollection.Add(value);
}
}
BCollection = new ObservableCollection<string>(returnedCollection);
}
}
You need of course to implement the INotifypropertyChanged Interface, so that the B Combobox Itemsource will be updated,
Now regarding the Xaml, due to some limitations in Xceed you need to specify the Combobox's ItemSource and SelectedItem using the RelativeSource and Ancestor binding,
<Grid >
<xcdg:DataGridControl ItemsSource="{Binding GridItemsCollection}" AutoCreateColumns="False" SelectionMode="Single" >
<xcdg:DataGridControl.Columns>
<xcdg:Column Title="A"
FieldName="A"
>
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
<xcdg:Column.CellEditor>
<xcdg:CellEditor>
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<ComboBox Name="AComboBox" SelectedItem="{Binding SelectedAItem, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}" SelectedValue="{xcdg:CellEditorBinding}"
ItemsSource="{Binding RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type wpfApplication3:MainWindow}},
Path=ACollection}">
</ComboBox>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
</xcdg:Column.CellEditor>
</xcdg:Column>
<xcdg:Column Title="B"
FieldName="B"
>
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
<xcdg:Column.CellEditor>
<xcdg:CellEditor>
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<ComboBox Name="AComboBox" SelectedValue="{xcdg:CellEditorBinding}" ItemsSource="{Binding RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type Window}},
Path=BCollection}">
</ComboBox>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
</xcdg:CellEditor>
</xcdg:Column.CellEditor>
</xcdg:Column>
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
</Grid>
and the result is something like that
The Other way to do that is by using a MultivalueConverter and update the B Collection eachtime the A SelectedValue is changed,
something like that :
<xcdg:CellEditor.EditTemplate>
<DataTemplate>
<ComboBox Name="AComboBox" SelectedValue="{xcdg:CellEditorBinding}">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource BCollectionConverter}">
<Binding Path="BCollection" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
<Binding Path="SelectedValue" ElementName="AComboBox" />
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</DataTemplate>
</xcdg:CellEditor.EditTemplate>
And implement the converter to update the B Combobox's ItemSource,
public class BCollectionConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null)
return null;
var bCollection = (values[0] as ObservableCollection<String>);
var aSelectedItem = (values[1] as String);
if (aSelectedItem == null)
return null;
var returnedCollection = new ObservableCollection<String>();
foreach (var value in bCollection)
{
if (value.StartsWith(aSelectedItem))
{
returnedCollection.Add(value);
}
}
return returnedCollection;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I didn't try that last one, you might as well give it a try, I hope that did help.
I would like to set the bottom Corner Radius of a ListView Item for just the "last" item in the list. I've attempted to do so with a Converter (which in fact finds the last row), but to no avail.
The desirable effect is when the Converter returns true after finding the last item in the ListView, the border CornerRadius on the last ListViewItem is set to CornerRadius="0,0,10,10". For all other items in the ListView, CornerRadius="0,0,0,0"
What I've done so far.
The Converter...
public class IsLastItemConverter : IValueConverter
{
#region IValueConverter Members
public object TrueValue { get; set; }
public object FalseValue { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ListViewItem item = value as ListViewItem;
ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
if (listView != null)
{
int index = listView.ItemContainerGenerator.IndexFromContainer(item);
if (index == listView.Items.Count - 1)
{
TrueValue = true;
return (bool)TrueValue;
}
else
{
FalseValue = false;
return (bool)FalseValue;
}
}
FalseValue = false;
return (bool)FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Just Convert Back
return true;
}
#endregion
}
The XAML...
<local:IsLastItemConverter x:Key="lastItemConverter"
TrueValue="0,0,10,10" FalseValue="0,0,0,0"/>
<DataTemplate x:Key="CellContentTemplate">
<Border
x:Name="CellContentBorder"
Background="{StaticResource GrayCharcoal}"
BorderThickness="4,4,4,4"
BorderBrush="{StaticResource Gray}"
CornerRadius="{Binding Converter={StaticResource lastItemConverter},
RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}}}"
HorizontalAlignment="Left"
Width="210"
Height="50"
ContextMenu="{StaticResource MarginalUnitsContextMenu}"
ScrollViewer.VerticalScrollBarVisibility="Hidden">...
Thoughts and ideas much appreciated - Glenn
I would try returning a real CornerRadius instead of a string (which, you are essentially returning).
My assumption is following:
When passing as string "0,0,0,0" in xaml as CornerRadius, the appropriate Converter is used to convert this string in to a CornerRadius struct.
As you are now using a custom converter, possibly the string to CornerRadius Converter is not anymore used...
EDIT (in light of Clemens answer): The Converter for the property does seem to kick in when a string was returned from the custom converter associated with the binding!
This is because CornerRadius has an associated TypeConverter:
[TypeConverterAttribute(typeof(CornerRadiusConverter))]
public struct CornerRadius : IEquatable<CornerRadius>
which handles this conversion. This converter handles the Conversion from string to CornerRadius.
(Taken from http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverterattribute.aspx )