I'm new to the whole data binding thing with XAML for WP8.1. I've been searching on the internet and I can't seem to find a straight-forward answer to my problem.
I have a ListBox control on my page with 3 columns. What I need to do is fill each column with it's respective data.
I have a class that defines a "CompleteStation" which is made up of 3 strings (StationName, Distance, and GasPrice)
This is the XAML code for the ListBox... How am I able to pass the ListBox a CompleteStation (3 strings) and have it separate each string into the correct column?
This is my XAML code below. In my main class, I'm just adding a CommpleteStation item to the ListBox.
Also, do I need to reference the CompleteStation class in the XAML code? If yes, how do I do this?
<ListBox Name="listBoxStations" FontSize="20" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="480" Background="#e6e6e6" Margin="0,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Station}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Distance}"/>
<TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Price}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks!
-Johan
Lets say your CompleteStation is an ObservableCollection which implements NotifyPropertyChanged, Then XAML should be as follows
<ListBox Name="listBoxStations" ItemSource={Binding CompleteStation} FontSize="20" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="480" Background="#e6e6e6" Margin="0,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Station}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Distance}"/>
<TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Price}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Your Model:
public sealed class CommpleteStation
{
public string Station{get;set;}
public string Distance{get;set;}
public string Price{get;set;}
}
Your ViewModel:
public sealed class MyViewModel
{
public ObservableCollection<CommpleteStation> CommpleteStations{get;private set;}
public MyViewModel()
{
CommpleteStations = new ObservableCollection<CommpleteStation>();
CommpleteStations.Add(new CommpleteStation{Station="One", Distance="15",Price="130"};
}
}
Then,
IN codebehind for your UI, the "View", you instantiate your ViewModel and set it as the DataContext for your View.
public MyView()
{
this.DataContext = new MyViewModel();
}
Related
I have a TabView whose ItemTemplate is like this:
<controls:TabView.ItemTemplate>
<DataTemplate x:DataType="data:Playlist">
<local:HeaderedPlaylistControl
IsPlaylist="True"
Loaded="HeaderedPlaylistControl_Loaded"
MusicCollection="{x:Bind Mode=OneWay}" />
</DataTemplate>
</controls:TabView.ItemTemplate>
This is part of the HeaderedPlaylistControl:
<local:PlaylistControl
AllowReorder="False"
AlternatingRowColor="True"
ItemsSource="{x:Bind MusicCollection.Songs, Mode=OneWay}">
<local:PlaylistControl.Header>
<controls:ScrollHeader Mode="Sticky">
<UserControl>
<Grid
x:Name="PlaylistInfoGrid"
Padding="10"
Background="{ThemeResource SystemColorHighlightColor}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
x:Name="PlaylistCover"
Grid.RowSpan="3"
Width="180"
Height="180"
Margin="20"
Source="Assets/monotone_bg_wide.png" />
<TextBlock
x:Name="PlaylistNameTextBlock"
Grid.Column="1"
Margin="0,5"
VerticalAlignment="Center"
FontSize="36"
Foreground="White"
Style="{StaticResource HeaderTextBlockStyle}"
Text="{x:Bind MusicCollection.Name, Mode=OneWay}" />
<TextBlock
x:Name="PlaylistInfoTextBlock"
Grid.Row="1"
Grid.Column="1"
Margin="0,5"
VerticalAlignment="Top"
Foreground="White"
Text="{x:Bind MusicCollection.Songs, Converter={StaticResource SongCountConverter}, Mode=OneWay}" />
</Grid>
</UserControl>
</controls:ScrollHeader>
</local:PlaylistControl.Header>
</local:PlaylistControl>
When I switch between tabs, the HeaderedPlaylistControl doesn't update it's content. Why is that?
Is it because of the MusicCollection property (it is of type Playlist) doesn't notify the binding when switching tabs? If so, where should I put the notification? The definition of Playlist is here.
HeaderedPlaylistControl is here:
Yes, as you suspected, the problem is that the MusicCollection property is an ordinary property which does not notify about changes. To make your code work, you need to make the MusicCollection property a dependency property (see docs). This is a kind of properties which are best for data-bound properties of visual controls and have many additional features as well.
public static readonly DependencyProperty MusicCollectionProperty =
DependencyProperty.Register(
nameof(MusicCollection), typeof(Playlist),
typeof(HeaderedPlaylistControl), null
);
public Playlist MusicCollection
{
get { return (bool)GetValue(MusicCollectionProperty); }
set { SetValue(MusicCollectionProperty, value); }
}
the Xaml looks like this:
<ListBox Name="lbEurInsuredType" HorizontalContentAlignment="Stretch" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
<!-- <ColumnDefinition Width="20"></ColumnDefinition>-->
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}"></TextBlock>
<TextBox Text="{Binding Uw}"></TextBox>
<!-- <TextBox Text="{Binding Partner}"></TextBox>-->
</Grid>
</DataTemplate></ListBox.ItemTemplate>
</ListBox>
then in my code behind I have :
public DefaultSettings()
{
InitializeComponent();
List<EurItem> items = new List<EurItem>
{
new EurItem() { Title = "Couple", Uw = 190m, Partner = 170m },
new EurItem() { Title = "Family", Uw = 180m, Partner = 160m }
};
lbEurInsuredType.ItemsSource = items;
}
What happens is that If I Only have the TextBlock in the xaml, then the title shows in the listbox, as soon as I start to introduce other members, then it displays the last item that has been bound and looses all the others.
This is unnecessary because you are already directly setting it in your code behind.
ItemsSource="{Binding}"
Also, you are not setting any Grid.Column definitions on your succeeding elements. So they are overlapping each other.
You might want something like this
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}"></TextBlock>
<TextBox Text="{Binding Uw}" Grid.Column="1"></TextBox>
<TextBox Text="{Binding Partner}" Grid.Column="2"></TextBox>
</Grid>
You're placing several controls in a Grid with several columns. By default, each item you place inside a Grid is shown in the first row and first column.
Since you're not telling the controls otherwise, all your controls are being added to the first row and column, overlapping each other... So only the last control is shown.
You need to use Grid.Column to specify which column should each control be placed in.
<ListBox Name="lbEurInsuredType" HorizontalContentAlignment="Stretch" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="30"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}"></TextBlock>
<TextBox Text="{Binding Uw}" Grid.Column="1"></TextBox>
<TextBox Text="{Binding Partner}" Grid.Column="2"></TextBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have made a simple RSS Feed app using AppStudio, and as the title suggests, I need some help on implementing DataTemplateSelector.
I want the first and the last items of the feed to use "BigCards" item template, while the others to have the "SmallCards" item template.
Here is the code for the item template styling:
<!-- BigCards Item -->
<DataTemplate x:Key="BigCards">
<Grid Style="{StaticResource BoxGrid}" Margin="0,0,0,10" Height="380">
<Rectangle Width="900"/>
<Grid Style="{StaticResource BoxGrid}">
<Grid.RowDefinitions>
<RowDefinition Height="280"/>
<RowDefinition Height="220"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding ImageUrl}" Stretch="UniformToFill" Margin="0,4,0,0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Grid Grid.Row="1" Height="220" Margin="10,10,10,10">
<Grid Height="210">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource BoxTitleStyle}" Text="{Binding Title}" MaxLines="2"/>
</Grid>
</Grid>
</Grid>
</Grid>
</DataTemplate>
<!-- SmallCards Item -->
<DataTemplate x:Key="SmallCards">
<Grid Height="120" Margin="0,0,0,10" Style="{StaticResource BoxGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageUrl}" Stretch="UniformToFill" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Grid Grid.Column="1">
<Rectangle Width="900" Height="0"/>
<Grid Margin="16,5,16,5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource BoxTitleStyle}" Text="{Binding Title}" MaxLines="2"/>
<TextBlock Grid.Row="1" Margin="0,5,0,0" Style="{StaticResource BoxSubtitleStyle}" Text="{Binding Summary}"/>
</Grid>
</Grid>
</Grid>
Here is some code behind Mainpage.xaml.cs which is needed as a trigger between these item templates:
//Listim dinamik i artikujve
public abstract class TemplateSelector : ContentControl
{
public abstract DataTemplate SelectTemplate(object item, DependencyObject container);
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
ContentTemplate = SelectTemplate(newContent, this);
}
}
//endof Abstract Class
public class ArticleTemplateSelector : TemplateSelector
{
public DataTemplate BigCards
{
get;
set;
}
public DataTemplate SmallCards
{
get;
set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
// Determine which template to return;
return null;
}
}
I would really appreciate if someone could help me on what I should insert in the DataTemplate SelectTemplate class so the first and the last item on my RSS Feed will use BigCards template, while the others will use SmallCards...?
Here is the Main Page xaml which as for the moment uses the BigCards template:
<Hub x:Name="Container" Grid.Row="1" Margin="0,28,0,0" Background="{StaticResource AppBackground}" DataContext="{Binding}" SectionsInViewChanged="OnSectionsInViewChanged">
<HubSection x:Name="LajmetSection" Padding="0,-30,20,0" Width="400" DataContext="{Binding MainViewModel.LajmetModel}"
d:DataContext="{d:DesignData Source=/Assets/Data/LajmetDataSource.json, Type=vm:LajmetViewModel, IsDesignTimeCreatable=true}"
ContentTemplate="{StaticResource BigCards}" IsHeaderInteractive="{Binding HasMoreItems}" />
</Hub>
Regards
I solved it in a different way. I modified the RSS page of my website in a way which would assign a static author for every 10th post. In my case I assigned every 10th post to "X" author. Then I implemented this simple statement
if (RssSchema.Author==X)
use BigCards;
else
use SmallCards;
which does the job quite nicely. If anyone needs help feel free to contact me.
I'm using WPFLocalizationExtension to localize a C#/ .Net4.5 application but I didn't manage to localize DropDown Menus with custom DataTemplate because I can't use the DisplayMemberPath. For ordinary dropdown localization works like this:
<telerik:RadComboBox ItemsSource="{Binding GlassColors}"
SelectedValue="{Binding Ampule.ID_GlassColor, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="ID_GlassColor"
DisplayMemberPath="{lang:Loc dmp_GlassColor}"/>
The ComboBox example above is linked to a database table containing to language. The displayed language is changed by the localized DisplayMemberPath. This approach is very easy and I can recommend it to everyone else. How ever it doesn't work for comboboxes using a custom DataTemplate. See example below:
<telerik:RadComboBox ItemsSource="{Binding PackagingTypesFilter}"
SelectedValue="{Binding SelectedPackagingTypeFilter}"
SelectedValuePath="ID_PackagingType">
<telerik:RadComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type model:Tbl_PackagingMaster_ID_PackagingType}">
<Grid VerticalAlignment="Center" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding TypeIcon, Converter={StaticResource StringToUriConverter}}" VerticalAlignment="Top" Grid.Column="0" Height="14" Margin="2" />
<TextBlock Text="{Binding PackagingTypeDescription}" Style="{StaticResource TextBlockMediumSmallBlackStyle}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
The ItemSource is a Collection where PackagingTypeDescription contains the English and PackagingTypeDescriptionGerman contains the German description.
How can I localize the code sample above?
I solved this using a TemplateSelector.
Here is the TemplateSelector class:
using System.Windows;
using System.Windows.Controls;
namespace Common.TemplateSelector
{
public class LanguageTemplateSelector : DataTemplateSelector
{
public DataTemplate TemplateEnglish { get; set; }
public DataTemplate TemplateGerman { get; set; }
public const string LanguageIdentifier = "de";
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return Configuration.Configuration.Language == LanguageIdentifier ? this.TemplateGerman : this.TemplateEnglish;
}
}
}
And here the template definition and the definition of the TemplateSelector defined in the <UserControl.Resources></UserControl.Resources> area:
<DataTemplate DataType="{x:Type model:Tbl_PackagingMaster_ID_PackagingType}" x:Key="PackagingTypeEnglish">
<Grid VerticalAlignment="Center" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding TypeIcon, Converter={StaticResource StringToUriConverter}}" VerticalAlignment="Top" Grid.Column="0" Height="14" Margin="2" />
<TextBlock Text="{Binding PackagingTypeDescription}" Style="{StaticResource TextBlockMediumSmallBlackStyle}" Grid.Column="1"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type model:Tbl_PackagingMaster_ID_PackagingType}" x:Key="PackagingTypeGerman">
<Grid VerticalAlignment="Center" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding TypeIcon, Converter={StaticResource StringToUriConverter}}" VerticalAlignment="Top" Grid.Column="0" Height="14" Margin="2" />
<TextBlock Text="{Binding PackagingTypeDescriptionGerman}" Style="{StaticResource TextBlockMediumSmallBlackStyle}" Grid.Column="1"/>
</Grid>
</DataTemplate>
<templateSelector:LanguageTemplateSelector x:Key="PackagingTypeLanguageSelector"
TemplateEnglish="{StaticResource PackagingTypeEnglish}"
TemplateGerman="{StaticResource PackagingTypeGerman}" />
This is how I use it for the ComboBox:
<telerik:RadComboBox ItemsSource="{Binding PackagingTypesFilter}"
SelectedValue="{Binding SelectedPackagingTypeFilter}"
SelectedValuePath="ID_PackagingType"
ItemTemplateSelector="{StaticResource PackagingTypeLanguageSelector}" />
However, I'm still looking for a more elegant way to solve this.
I gave up on my code bugs so I decide to write here. I really hope to get a solution. What I want with the listbox is: when i click the button, it will retrieve data from database then load it into the listbox. It worked fine. But when I add wpf style, it started problem because I want to add image into each item next to text - image (right side) and text (left side). The result in the listbox is blank but actually it seems there is a data list - please look at the picture. I may have done something wrong in my code or wpf. I am not sure what is problem.... I would appreciate if you can have a look at my code. Your given code would be much helpful. Thanks alot!
WPF:
<ListBox Name="lstDinner" DisplayMemberPath="Name" Margin="513,85,608,445" Style="{DynamicResource ResourceKey=styleListBox}"/>
WPF STYLE:
<DataTemplate x:Key="templateListBoxItem">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Margin="0,0,10,0">
<Image Source="{Binding Path=Image}"
Stretch="Fill"
Height="40"
Width="40"></Image>
</Border>
<TextBlock Text="{Binding Path=Name}"
FontWeight="Bold"
Grid.Column="1"
Grid.Row="0"></TextBlock>
<TextBlock Text="{Binding Path=About}"
Grid.Column="1"
Grid.Row="1"></TextBlock>
</Grid>
</DataTemplate>
<Style x:Key="styleListBox" TargetType="{x:Type ListBox}">
<Setter Property="ItemTemplate" Value="{StaticResource ResourceKey=templateListBoxItem}"></Setter>
</Style>
C#:
private void Button_Click(object sender, RoutedEventArgs e)
{
_dinnerExtractor = new DinnerExtractor();
const int oneDay = 1;
_databaseDinnerList = new ObservableCollection<FoodInformation>(_dinnerExtractor.GetDinnerDays(oneDay));
if (_databaseDinnerList != null)
{
foreach (var list in _databaseDinnerList)
{
lstDinner.Items.Add(new FoodInformation { Dinner = list.Dinner, DinnerImage = list.DinnerImage});
}
//lstDinner.ItemsSource = _databaseDinnerList;
}
}
FoodInformation class does not contains properties: Image and Name (you are trying binding to these properties in DataTemplate).
From code-behind we can create definition of FoodInformation class with properties Dinner and DinnerImage:
class FoodInformation
{
public string Dinner { get; set; }
public ImageSource DinnerImage { get; set; }
}
So you should binding to properties Dinner and DinnerImage, not to Image and Name.
If you change in DataTemplate appropriate properties names everything will be ok.
<DataTemplate x:Key="templateListBoxItem">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Margin="0,0,10,0">
<Image Source="{Binding Path=DinnerImage }"
Stretch="Fill"
Height="40"
Width="40"></Image>
</Border>
<TextBlock Text="{Binding Path=Dinner }"
FontWeight="Bold"
Grid.Column="1"
Grid.Row="0"></TextBlock>
</Grid>
</DataTemplate>