Don't render Image when Image.Source is empty - xaml

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.

Related

IsVisible Binding is not working in Xamarin.Forms

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..

How to display a label with click on listview

I want to show a label when i click on my item in my listview.
The real problem i don't know how to link between my viewmodel and my views
I want modify my label in viewmodel but I don't know if its possible currently.
My xaml :
<StackLayout>
<Label x:Name="labelperso"
Text="{Binding newProduct}"
IsVisible="{Binding Addproduct}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
BackgroundColor="#000000"
FontSize="20"
Opacity="0"/>
<ListView ItemsSource="{Binding Products}" CachingStrategy="RecycleElement" RowHeight="50" >
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding CodeReferenceLibelle}" TextColor="Black"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<b:EventToCommandBehavior EventName="ItemSelected" Command="{Binding
SelectCommand}" Converter="{StaticResource SelectedItemConverter}"/>
</ListView.Behaviors>
my viewmodel :
#region labelperso property
private string _newProduct;
public string newProduct
{
get { return _newProduct; }
set { SetProperty(ref _newProduct, value); }
}
#endregion
#region Addproduct property
private bool _Addproduct;
public bool Addproduct
{
get { return _Addproduct; }
set { SetProperty(ref _Addproduct, value); }
}
#endregion
when I click on my item :
async Task Select()
{
newProduct = "Produit ajouté !";
basketManager.AddProductSkuAsync(sku);
newProduct = "";
await Task.Run(() => ShowText());
}
//I have tried this but I can't use my label in my view
async Task ShowText()
{
await labelperso.FadeTo(1);
await Task.Delay(1000);
await labelperso.FadeTo(0);
}
Why are you want to take the label "labelperso" in VM ? you can use it in xaml.cs instead.
You just need to add the event ItemSelected like this:
<ListView ItemsSource="{Binding Products}" ItemSelected="OnSelection">
In xaml.cs
void OnSelection(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null)
{
return;
}
//suppose the binding Object is Product
Product product = (Product)e.SelectedItem;
//labelperso.Text = "name = " + product.Name;
labelperso.FadeTo(1);
Task.Delay(1000);
labelperso.FadeTo(0);
}
Normally, VM are unrelated to Xaml, and we should not get labels from VM.
And we don't recommend it.But if you must, you can pass the Label in from the xaml.cs file like this:
You can define a variable in yourpage.xaml.cs:
public Label pageLabel;
and initial like this:
pageLabel = labelperso;
BindingContext = new YourViewmodel(this);
And in YourViewmodel.cs:
public Label ss;
public YourViewmodel(ContentPage parentPage)
{// here HomePage is your contentPage name of the page`
ss = ((HomePage)parentPage).pageLabel;//after this you can use it
}
You need to add a SelectedProduct property to your VM.
private string _SelectedProduct;
public string SelectedProduct
{
get { return _SelectedProduct; }
set { SetProperty(ref _SelectedProduct, value); }
}
You can then bind your ListView's SelectedItem to it
<ListView ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct}"
CachingStrategy="RecycleElement"
RowHeight="50" >
You can then control the visibility of your label by binding to SelectedProduct via a "nullToVisibility" converter, or by using triggers etc.
You should try to use MVVM pattern rather than hacking with code behind.
Using MVVM you can add a Visible property to your viewmodel and bind the IsVisible property of the label to it.
Code will be much easy to read and maintain.

XAML set image property to values using DependencyProperty

I have a model like:
{
"images": [{
"image_url": "..........",
"orientation": "horizontal"
},
{
"image_url": "...............",
"orientation": "vertical"
}]
}
and a ListView DataTemplate
<DataTemplate x:DataType="model:ImageItem">
<controls:ImageEx Source="{x:Bind cover_image_url}" PlaceholderSource="" PlaceholderStretch="Uniform"/>
</DataTemplate>
Now, I want the ImageEx control's PlaceholderSource property change along with orientation.
More clearly, when the image property is horizontal, ImageEx will load a place holder 1. When the image property is vertical, ImageEx will load a place holder 2.
How to do this. Please show me.
More clearly, when the image property is horizontal, ImageEx will load a place holder 1. When the image property is vertical, ImageEx will load a place holder 2.
You could use IValueConverter interface to approach, use converter class to converter different message type to different PlaceholderSource for ImageEx. You could refer the follow ImageConverter.
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
object img = null;
switch (value.ToString())
{
case "horizontal":
img = new BitmapImage(new Uri("ms-appx:///Assets/holder1.png"));
break;
case "vertical":
img = new BitmapImage(new Uri("ms-appx:///Assets/holder2.png"));
break;
default:
break;
}
return img;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Xaml
<Page.Resources>
<local:ImageConverter x:Key="Converter"/>
</Page.Resources>
......
<DataTemplate x:DataType="model:ImageItem">
<controls:ImageEx Source="{x:Bind cover_image_url}" PlaceholderSource="{x:Bind orientation,Converter={StaticResource Converter}}" PlaceholderStretch="Uniform"/>
</DataTemplate>

Getting content of checkboxes in xaml.cs in Silverlight

I am new to silverlight. I am trying to generate a list of checkboxes(with content). The idea is that the user will select some of these checkboxes and will press a button. Then we try to read the content of selected checkboxes for further processing. I don't know how many number of checkboxes will be there and therefore I can't use bindings.
This is the code snippet in the .xaml file.
<StackPanel Grid.Row="21" Grid.Column="1" Margin="5" VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ItemsControl Name="infoPairItems" ItemsSource="{Binding InfoPair}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Grid.Column="0" Name="infoPairSelectBox" IsEnabled="True" IsThreeState="False"
Margin="0,5" FontSize="12" IsChecked="bool"
Content="{Binding Converter={StaticResource infoPairToStringValueConverter}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</StackPanel>
I am trying to access these checkboxes in the .xaml.cs file like this.
foreach(var infoPairItem in infoPairItems.Items)
{
ContentPresenter container = infoPairItems.ItemContainerGenerator.ContainerFromItem(infoPairItem) as ContentPresenter;
if(container == null)
{
DebugLog.Log("container is null ");
}
DataTemplate dataTemplate = container.ContentTemplate;
CheckBox checkBox = (CheckBox)dataTemplate.LoadContent();
if (checkBox == null)
{
DebugLog.Log("checkBox is null !!!");
return;
}
if (checkBox.IsChecked.HasValue)
{
if (checkBox.IsChecked.Value)
{
DebugLog.Log("checkbox value true");
}
else
{
DebugLog.Log("checkbox value false");
}
}
}
The log 'checkbox value false' is always getting printed for all the checkboxes even when some of them are selected. I tried to use the debugger. It looks like that variable container is getting loaded with the correct value. Either the method LoadContent() is not working or I am using the wrong method.
I apologize beforehand if it is a repeat question. I tried to look into the previous questions on stackoverflow but could not find any answer. Please guide me in correct direction.
I will explain what happens and how to solve:
1.- You are getting the datatemplate not instances of the datatemplate, in case you want to manage the instances you can do by using the Loaded Event to add items to the List to create and update for instance a List.
2.- What makes all of these events a really complex code to manage is easier if you create the following:
2.1 A class for instance that has a bool and a string for the content with INotifyPropertyChanged:
public class InfoSelection : Model
{
Property bool for Selected
Property string for Info, or whatever and the converter
}
2.2 A list with the items you need of the type of that class in the DataContext
public List<InfoSelection> {get;set;}
(If you initialize just once in the constructor for instance, you do not need to implement INotiyPropertyChanged, just clear or removeitems, never reassign)
2.3 In the Xaml binding change to the following:
<CheckBox Grid.Column="0"
Name="infoPairSelectBox"
IsEnabled="True"
IsThreeState="False"
Margin="0,5"
FontSize="12"
IsChecked="{Binding Selected, Mode=TwoWay}"
Content="{Binding Info}"/>
I don't know how many number of checkboxes will be there and therefore I can't use bindings.
Incorrect.
To Visually display two levels of data generically, the use of a ItemsControl with individual DataTemplate`s for the parent items and their child items can be done.
Then to allow for the editing (your deletion operation) one needs to identify who the parent node is from the child nodes, along with getting the state of the checkbox.
That identification requires us to project the initial data into a wrapper class to facilitate binding/identification.
Let me explain.
Say our data displays a top level last name and all first names associated with the last name.
The above simulates a top level checkbox (to delete all) and children checkbox (to delete an individual item) for the following data class retrieved from the database:
public class VectorStrings
{
public int Id { get; set; }
public string LastName { get; set; }
public List<string> FirstNames { get; set; }
}
With simulated data loaded as such:
LastNames = new List<VectorStrings>()
{
new VectorStrings() { Id=9, LastName="Smith", FirstNames = new List<string>() { "Bob", "Victoria" } },
new VectorStrings() { Id=12, LastName="Jones", FirstNames = new List<string>() { "Peter", "Paul", "Mary" } },
};
Now for display I can generically display those items to the above data, but because we need to operate on the child data, we need to project that information into a holding wrapper class.
public class VectorCheckbox
{
public int Id { get; set; }
public int ParentId { get; set; }
public string DisplayName { get; set; }
public List<VectorCheckbox> Children { get; set; }
public object Tag { get; set; } // Same concept as a visual control property 'Tag'
}
so our code to project the original data looks like this:
CheckBoxData =
LastNames.Select(ln => new VectorCheckbox()
{
DisplayName = ln.LastName,
Id = ln.Id,
Tag = ln,
Children = ln.FirstNames.Select((fn, index) => new VectorCheckbox()
{
Id = index,
ParentId = ln.Id,
DisplayName = fn,
Tag = ln.Id // Hold the parent Id for identification
}).ToList()
})
.ToList();
Sweet! Now we just need nested ItemControl classes to display our data:
<ItemsControl ItemsSource="{Binding CheckBoxData}">
<ItemsControl.Resources>
<system:String x:Key="Format">Delete All for ID: '{0}' </system:String>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Margin="10" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="6,0,0,0">
<CheckBox Tag="{Binding Id}" Margin="0,0,0,10" Click="DeleteAll_Click">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id, StringFormat={StaticResource Format}}"/>
<TextBlock Text="{Binding DisplayName}" Margin="4,0,6,0"/>
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Tag="{Binding Tag}"
Content="{Binding DisplayName}"
Click="DeleteIndividual_Click"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</CheckBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
Now in code behind we subscribe to the click events. I use a message box to show that I have identified the right items. For example if one clicks on a delete all check box, it identifies the children and the state of the checkbox, and if I click on a child it identifies its parent and itself.
Parent Click
private void DeleteAll_Click(object sender, RoutedEventArgs e)
{
var cb = sender as CheckBox;
if (cb != null)
{
var id = (int)cb.Tag;
var nameInstance = ViewModel.LastNames.FirstOrDefault(nm => nm.Id == id);
if (nameInstance != null)
MessageBox.Show(string.Format("Delete all for {0} of names {1} (Status {2})",
nameInstance.LastName,
string.Join(",", nameInstance.FirstNames),
((cb.IsChecked ?? false) ? "Checked" : "UnChecked")
));
}
}
Child Click
private void DeleteIndividual_Click(object sender, RoutedEventArgs e)
{
var cb = sender as CheckBox;
if (cb != null)
{
var parentId = (int)cb.Tag; // Parent Id
var nameInstance = ViewModel.LastNames.FirstOrDefault(nm => nm.Id == parentId);
if (nameInstance != null)
MessageBox.Show(string.Format("Delete individual for {0} of name {1} (Status {2})",
nameInstance.LastName,
cb.Content,
((cb.IsChecked ?? false) ? "Checked" : "UnChecked")
));
}
}
So from that I have identified the checkbox state along with the target original items. This code simulates ultimately what you want to do. I leave the actual plumbing of the observable collection remove items up to you. But this gets the idea across.
I recommend that you experiment in WPF then take it to Silverlight, for the concepts are the same, but its easier/faster to test out in WPF.

LineBreaks in WCF bound textblock

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}}" />