Is it possible to concatenate binding text with a static text in source image
For example:
<Image Name="ImagePlace" Source="http://site.com/image/architecture.png" Grid.Column="0" />
<Image Name="ImagePlace" Source="{Binding Path=Ico}" Grid.Column="0" />
And i would like to concatenate the two sources.
I have a list of object named Category that contains a field Icon that contains for example "architecture.png". I bind it in a list.
The url of site doesn't change but the image changes always.
<ListBox x:Name="ListboxCategories">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Name="category" Tag="{Binding Path=Id}" Tap="category_Tap_1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Name="ImagePlace" Source="http://www.test.com/assets/images/icons/tags/architecture.png" Grid.Column="0" />
<TextBlock Text="{Binding Path=Title}" Grid.Column="1" HorizontalAlignment="Center" />
<Image Name="chevron" Grid.Column="2" Source="/Assets/AppBar/White/appbar.chevron.right.png" HorizontalAlignment="Right" Width="50" Height="50" />
</Grid>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The cleanest way (well, depending on your architecture) would be to add a property in your object to do the concatenation:
public string FullUri
{
get
{
return "http://site.com/image/" + this.Ico
}
}
Another way is to use a custom converter to do the concatenation.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
{
return null;
}
return "http://site.com/image/" + value.ToString();
}
Related
I've been working with XAML for some time now, but when I started I didn't knew much about MVVM so I avoided it. So all the logic in my projects is written in .xaml.cs instead of ViewModel.cs. Now the project is big and I have to move some parts of it to MVVM per request and I have no clue how to do so or where to actually start.
about the problem:
I have two Entities. lets call them A and Secret.
A can have Secret. If A does have it I need to display Secret.Name and Secret.Description beside the A data.
They are binded together with: A.Secret == Secret.Id.
A data and B data can change independently and is received/changed over API calls, but A.Secret and Secret.Id bind will never change.
Secrets is just an array of SecretModel elements
<ListBox ItemsSource="{Binding A, ElementName=uc}" SelectedItem="{Binding SelectedA, ElementName=uc}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" MaxHeight="300"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="ID:" TextAlignment="Right" />
<TextBlock Grid.Column="1" Text="{Binding Id}"/>
<TextBlock Grid.Row="1" Text="Type:" TextAlignment="Right"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Type}" />
<TextBlock Grid.Row="2" Text="Secret:" TextAlignment="Right" Visibility="{Binding Secret, Converter={StaticResource StringToVisibilityConverter}}"/>
<TextBlock Grid.Row="2" Grid.Column="1" Visibility="{Binding Secret, Converter={StaticResource StringToVisibilityConverter}}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource SecretIdToNameConverter}">
<Binding Path="Secret"/>
<Binding Path="Secrets" ElementName="uc"/>
</MultiBinding>
</TextBlock.Text>
<TextBlock.ToolTip>
<MultiBinding Converter="{StaticResource SecretIdToDescriptionConverter}">
<Binding Path="Secret"/>
<Binding Path="Secrets" ElementName="uc" />
</MultiBinding>
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Converters looks like this.
public class SecretIdToDescriptionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] != null && values[0] != DependencyProperty.UnsetValue && values[0] is string SecretId && values[1] != null && values[1] != DependencyProperty.UnsetValue && values[1] is SecretModel[] Secrets)
{
var tmp = Secrets.FirstOrDefault(secret => secret?.Id == SecretId);
if (tmp != null)
return tmp.Description;
return null;
}
else
{
return null;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
If anyone could give me some help/pointers on how I should/could migrate from Converters of this type to MVVM or point me to some resource explaining it to me - that would be greatly appreciated.
The things that mostly boggles me:
How should I share/acces the data betwean .xaml.cs or .xaml and ViewModel.cs at this point?
How are calls made to retrieve/give such data?
Is it possible to create method that would return the corresponding SecretModel, so I could just use it after the call in .xaml?
like
<magic ItemsSource= getSecret(A.Secret) >
<TextBlock Text = {Binding Name}/>
</magic>
I have a template for HeaderItem with the imageSource in it, so all tabs have the same image. How can I bring an additional parameter to use different images?
Template
<ControlTemplate x:Key="HeaderControlTemplate">
<Grid HorizontalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Source="{converters:ImageResource Media.logo-pp.png}" Grid.Row="0" WidthRequest="25" />
<Label Grid.Row="1" Text="{TemplateBinding Text}" HorizontalTextAlignment="Center" StyleClass="tabViewHeaderTextColor"/>
<BoxView Grid.Row="2" IsVisible="{TemplateBinding IsSelected}"
StyleClass="tabViewHeaderBgColor"
HorizontalOptions="Fill"
HeightRequest="2" />
</Grid>
</ControlTemplate>
Tabs
<telerikPrimitives:RadTabView>
<telerikPrimitives:RadTabView.Items>
<telerikPrimitives:TabViewItem>
<telerikPrimitives:TabViewItem.Header>
<telerikPrimitives:TabViewHeaderItem Text="1" ControlTemplate="{StaticResource HeaderControlTemplate}"/>
</telerikPrimitives:TabViewItem.Header>
<telerikPrimitives:TabViewItem.Content>
//SomeContent
</telerikPrimitives:TabViewItem.Content>
</telerikPrimitives:TabViewItem>
<telerikPrimitives:TabViewItem>
<telerikPrimitives:TabViewItem.Header>
<telerikPrimitives:TabViewHeaderItem Text="2" ControlTemplate="{StaticResource HeaderControlTemplate}"/>
</telerikPrimitives:TabViewItem.Header>
<telerikPrimitives:TabViewItem.Content>
//SomeContent
</telerikPrimitives:TabViewItem.Content>
</telerikPrimitives:TabViewItem>
<telerikPrimitives:TabViewItem>
<telerikPrimitives:TabViewItem.Header>
<telerikPrimitives:TabViewHeaderItem Text="3" ControlTemplate="{StaticResource HeaderControlTemplate}"/>
</telerikPrimitives:TabViewItem.Header>
<telerikPrimitives:TabViewItem.Content>
//SomeContent
</telerikPrimitives:TabViewItem.Content>
</telerikPrimitives:TabViewItem>
<telerikPrimitives:RadTabView>
TabViewHeaderItem also doesn't include the Image Source. So I don't know how to solve this. Sure I can do 3 templates, but it's not what i want.
I assume that Media.logo-pp.png is one of the 3 images you want to show. You could use the following trick to avoid duplicating templates:
First of all create a converter that will convert the tab name into the target image.
public class TabToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string imageName;
switch (value.ToString())
{
case "1":
imageName= "logo-pp.png"
break;
case "2":
imageName= "logo-pp2.png"
break;
case "3":
imageName= "logo-pp3.png"
break;
default:
imageName= "logo-pp.png"
break;
}
return ImageSource.FromResource($"Media.{imageName}", this.GetType().GetTypeInfo().Assembly);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then change your template definition as follow:
add a new namespace for your converter class:
xmlns:converters="clr-namespace:Metadia.Converters"
add a static resource and reference the above converter
<ResourceDictionary>
<converters:TabToImageConverter x:Key="TabToImage" />
add a name to the label so it can be referenced
add binding to image source using the label's Text property and the converter
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Source="{TemplateBinding Source={x:Reference tabTitle}, Path=Text, Converter={StaticResource {TabToImage}}" Grid.Row="0" WidthRequest="25" />
<Label x:Name="tabTitle" Grid.Row="1" Text="{TemplateBinding Text}" HorizontalTextAlignment="Center" StyleClass="tabViewHeaderTextColor"/>
<BoxView Grid.Row="2" IsVisible="{TemplateBinding IsSelected}"
StyleClass="tabViewHeaderBgColor"
HorizontalOptions="Fill"
HeightRequest="2" />
</Grid>
Et voilà!
Note that if you rename the images the same as the tabs text, the converter could simply return (if not null):
return ImageSource.FromResource($"Media.{value.ToString()}", this.GetType().GetTypeInfo().Assembly);
Hope this helps & happy coding!
I have no idea how to achieve this, but I have a date and time column in a ListBox. The Date column should not display if the date was already in there. I know that in combination with ListCollectionView and Listview/DataGrids, it is propably possible. But can I achieve this with a ListBox and a List. Keep in mind I am using the MVVM principle. This is my listbox:
<ListBox Grid.Row="2" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding Schedules}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Background="Transparent">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding MyDateTime, StringFormat='d' }" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding MyDateTime, StringFormat='t'}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Grid.Row="0" Text="{Binding SomeText}" TextTrimming="WordEllipsis" LineStackingStrategy="MaxHeight" MaxHeight="20" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have made this in excel to give an example of what I am trying to achieve:
I want the affect where I have highlighted with yellow
Converters
// Order Schedules using System.Linq
public class ToOrderedListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<ScheduleItem> schedules = (List<ScheduleItem>)value;
var subset = from item in schedules
orderby item.MyDateTime.TimeOfDay
orderby item.MyDateTime.ToString("yyyy/MM/dd") descending
select item;
return subset.ToList();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
// Show only first occurrence of date
public class DateToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
DateTime currentItem = (DateTime)values[0];
List<ScheduleItem> schedules = (List<ScheduleItem>)values[1];
ScheduleItem firstOccurrence =
schedules.Find(item => item.MyDateTime.Year == currentItem.Year
&& item.MyDateTime.Month == currentItem.Month
&& item.MyDateTime.Day == currentItem.Day);
if (firstOccurrence.MyDateTime == currentItem)
return Visibility.Visible;
else return Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML
<ListBox Grid.Row="2" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Schedules, Converter={StaticResource ToOrderedListConverter}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Background="Transparent">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding MyDateTime, StringFormat='dd/MM/yyyy'}" HorizontalAlignment="Left" VerticalAlignment="Center">
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource DateToVisibilityConverter}">
<Binding Path="MyDateTime"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ListBox}}"
Path="ItemsSource"/>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding MyDateTime, StringFormat='t'}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Grid.Row="0" Text="{Binding SomeText}" TextTrimming="WordEllipsis" LineStackingStrategy="MaxHeight" MaxHeight="20" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can use this approach for your requirements.
https://code.msdn.microsoft.com/windowsdesktop/CollectionView-Tips-MVVM-d6ebb4a7#content
I have the following ItemTemplate:
<ListView.ItemTemplate>
<DataTemplate>
<Border CornerRadius="5" Background="{Binding SenderId, Converter={StaticResource ColorConverter}}"
MaxWidth="{Binding ActualWidth, ElementName=SideBar}"
HorizontalAlignment="{Binding SenderId, Converter={StaticResource AlignmentConverter}}">
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" />
</Border>
</DataTemplate>
</ListView.ItemTemplate>
What I would like to do is to bind the Border elements MaxWidth property to 70% of the SideBar's Width but I can't seem to find a way to do this in xaml.
Is there a clean way to achieve this?
To expand on Rohit Vats comment:
Add this class to your solution:
public class ElementSizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double percentage = double.Parse(parameter.ToString());
return double.Parse(value.ToString()) * percentage;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
Then, in your XAML, declare the namespace of where this class is in the document header:
xmlns:ConverterNamespace="clr-namespace:ConverterNamespace"
Instantiate the converter in the resources:
<Window.Resources>
<ConverterNamespace:ElementSizeConverter x:Key="ElementSizeConverter"/>
</Window.Resources>
And then you can use the following binding:
Width="{Binding Width, ElementName=elementName, Converter={StaticResource ElementSizeConverter}, ConverterParameter=0.7}"
Note: I couldn't get ActualWidth to work, but play around with this and see if this works for you.
By nesting in a grid you could also get the percentages:
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="70*" />
<ColumnDefinition Width="15*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="1"
CornerRadius="5"
Background="{Binding SenderId, Converter={StaticResource ColorConverter}}"
HorizontalAlignment="{Binding SenderId, Converter={StaticResource AlignmentConverter}}">
<TextBlock Text="{Binding Message}" TextWrapping="Wrap" />
</Border>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Need a small fix with comboboxitems in wpf. I want to show the comboboxitems or in the comboxbox (textblock) with an image beside. I couldn't get it done. Can someone help me with the fix please?
Thanks in advance
<ComboBox Name="avilibity" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="1" Source="/resources/icon.png" ></Image>
<TextBlock FontSize="14" Grid.Column="2" ></TextBlock>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>Available</sys:String>
<sys:String>Offline</sys:String>
<sys:String>Away</sys:String>
</ComboBox>
First Problem is that the Grid Column index is 0 based - means Image shold have Grid.Column = "0", TextBlock = "1".
Should it be allways the same image? Consider using of user-defined object as property with binding. Thus you can have individual icons.
May be one of the gurus here can provide XAML-only example... i can imagine only with code behind...
Here two possible solutions:
Code behind in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace ListImageWithText
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnNotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private List<ImageWithText> _objectlist;
public List<ImageWithText> ObjectList
{
get
{
return _objectlist;
}
set
{
_objectlist = value;
OnNotifyPropertyChanged("ObjectList");
}
}
public MainWindow()
{
InitializeComponent();
ImageWithText iwt;
this._objectlist = new List<ImageWithText>();
iwt = new ImageWithText();
iwt.Image = "App.ico";
iwt.Text = "Away";
this._objectlist.Add(iwt);
iwt = new ImageWithText();
iwt.Image = "App.ico";
iwt.Text = "Available";
this._objectlist.Add(iwt);
iwt = new ImageWithText();
iwt.Image = "App.ico";
iwt.Text = "Offline";
this._objectlist.Add(iwt);
OnNotifyPropertyChanged("ObjectList");
}
}
public class ImageWithText
{
public string Image { get; set; }
public string Text { get; set; }
}
}
Define small class with two public properties, present list of those objects as property and use binding in XAML
<ComboBox Name="avilibity"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1"
ItemsSource="{Binding ObjectList, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding Image}" />
<TextBlock FontSize="14" Grid.Column="1" Text="{Binding Text}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The simplest way with XAML repetitions - is use of ComboBox Items, i do not preffer this path...
<ComboBox Name="avilibity"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1">
<ComboBoxItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="App.ico" ></Image>
<TextBlock FontSize="14" Grid.Column="1" >Available</TextBlock>
</Grid>
</ComboBoxItem>
<ComboBoxItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="App.ico" ></Image>
<TextBlock FontSize="14" Grid.Column="1" >Offline</TextBlock>
</Grid>
</ComboBoxItem>
<ComboBoxItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="App.ico" ></Image>
<TextBlock FontSize="14" Grid.Column="1" >Away</TextBlock>
</Grid>
</ComboBoxItem>
</ComboBox>
There are few mistakes in your code:
1. Row & Column are start with index 0;
2. TextBlock need to set the Text Property, so that it can display.
If you just want to display the same icon,
this code will gonna work(this can fix your perblom):
<ComboBox Name="avilibity" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/resources/icon.png" ></Image>
<TextBlock FontSize="14" Grid.Column="1" Text="{Binding}"></TextBlock>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>Available</sys:String>
<sys:String>Offline</sys:String>
<sys:String>Away</sys:String>
</ComboBox>
But as I saw, there are three kind of state of the items.
Maybe you want to change the image when different item display.
Here is my solution:
<Window.Resources>
<converters:ImageSourceConverter x:Key="ImageSourceConverter" />
</Window.Resources>
<ComboBox Name="avilibity" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding Converter={StaticResource ImageSourceConverter}}" />
<TextBlock FontSize="14" Grid.Column="1" Text="{Binding}"></TextBlock>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>Available</sys:String>
<sys:String>Offline</sys:String>
<sys:String>Away</sys:String>
</ComboBox>
ImageSourceConverter:
public class ImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new BitmapImage(new Uri("/resources/" + value + ".png",UriKind.RelativeOrAbsolute));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}