I have List box in which I am displaying values at runtime. But i want to show an image in list box only when i selected an item from list box.Currently I am using DataTemplate and ItemTemplate in list box for displaying values at runtime (In short data binding).Thanks
One solution is to add a dependency property IsSelected to your item view model, and toggle that when you tap on an item. This would mean that you can select multiple items, i.e. show images from several rows at once.
using some xaml like this:
<phone:PhoneApplicationPage.Resources>
<convert:BooleanToVisibilityConverter x:Key="booltovisibility" />
</phone:PhoneApplicationPage.Resources>
<phone:PhoneApplicationPage.DataContext>
<vm:MainViewModel/>
</phone:PhoneApplicationPage.DataContext>
<Border Grid.Row="1" BorderThickness="1" BorderBrush="Red">
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="Green">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding ToggleSelected}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Grid.Row="0" Text="{Binding Text}"/>
<Image Grid.Row="1" Source="{Binding Image}" Visibility="{Binding IsSelected, Converter={StaticResource booltovisibility}}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
Note that this xaml uses the System.Windows.Interactivity dll from the Silverlight SDK. It also uses a custom BooleanToVisibilityConverter, which is a IValueConverter that converts a boolean to a Visibility enum value.
Further more, we can define an ItemViewModel like the one below, and have an "Items" property in the MainViewModel
private readonly ObservableCollection<ItemViewModel> items;
public ObservableCollection<ItemViewModel> Items { get { return items; } }
which is populated from code.
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Test.Commands;
namespace Test.ViewModels {
public class ItemViewModel : DependencyObject {
private ICommand toggleCommand;
public ItemViewModel(string title) {
Text = title;
Image = new BitmapImage(new Uri("graphics/someimage.png", UriKind.Relative));
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ItemViewModel), new PropertyMetadata(default(string)));
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(ItemViewModel), new PropertyMetadata(default(bool)));
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(ItemViewModel), new PropertyMetadata(default(ImageSource)));
public string Text {
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public bool IsSelected {
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
public ImageSource Image {
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public ICommand ToggleSelected {
get { return toggleCommand ?? (toggleCommand = new RelayCommand(o => IsSelected = !IsSelected)); }
}
}
}
where RelayCommand is similar to the one found at WPF Apps With The Model-View-ViewModel Design Pattern article. The main difference is that CommandManager is not available in the Windows Phone SDK.
By using this model you can tap on rows to select them, using the ToggleSelected command in the view model, and deselect them by tapping them again, in effect showing or hiding the image.
Related
I'm new to Xamarin framework and want to create an app using Master-Detail Page
I did simple Master-Detail Navigation page demo from xamarin websit
master-detail-page xamarin webise
only difference is I used ViewCell inside DataTemplate.In ViewCell I have Label
instead of Image.
after clicking on MasterPageItems navigation is working fine but now I want to change the label Text color also .
<ListView x:Name="listView" VerticalOptions="FillAndExpand" SeparatorVisibility="None" RowHeight="50" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Title}" TextColor="#1ca7ec" FontSize="18"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(ContactsPage)));
masterPage.ListView.SelectedItem = null;
IsPresented = false;
}
}
I think you can do in this way:
1- in your model you should have a "TextColor" property and a "Selected" property
public bool Selected { get; set; }
// I think you should not return "Color" type (for strong MVVM) but, for example, a value that you can convert in XAML with a IValueConverter...
public Color TextColor
{
get
{
if (Selected)
return Color.Black;
else
return Color.Green;
}
}
2- In your XAML you should have something like
<ListView SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding List}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Name}" TextColor="{Binding TextColor}" FontSize="18"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
3- and in your ViewModel something like
MyModel _selectedItem { get; set; }
public ObservableCollection<MyModel> List { get; set; } = new ObservableCollection<MyModel>();
public MyModel SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem != null)
_selectedItem.Selected = false;
_selectedItem = value;
if (_selectedItem != null)
_selectedItem.Selected = true;
}
}
When your item in the list is selected , SelectedItem property change and Selected property in your model became True or False, changing the TextColor property (I use PropertyChanged.Fody for INPC).
Hope this help
You can find the repo on GitHub
Instead of use a TextColor Property in your Model, I think you can also use only Selected property and an IValueConverter that convert Selected property to a color
I have a listpicker on my Category details page
<toolkit:ListPicker HorizontalAlignment="Left" Name="ListPickerCategoryTypes"
ItemsSource="{Binding CategoryTypes, Mode=TwoWay}" Header="Category Types;"
VerticalAlignment="Top" Width="438" Margin="9,6,0,0" SelectedItem="{Binding CategoryTypeName, Mode=TwoWay}" >
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryTypeName}" Tag="{Binding Id}"></TextBlock>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
The list picker populates correctly but when I navigate to the details page the selectedItem is never set?
I have a category Name textbox that is correctly displaying the category name that was selected so I know it has the data just not sure what I am doing wrong
with the listpicker. I thought maybe it was that I wasnt using the CategoryTypeName I was trying to use the Category Type ID that is on my model.
I am using MVVM so I would like to be able to do this in my view model.
Addtional Code to help
The SettingProduct view lists all the products I have in a listbox.
<Grid x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<ListBox x:Name="TileList" ItemTemplate="{StaticResource TileProductDataTemplate}"
ItemsSource="{Binding DisplayProducts}"
Margin="6,20,6,-8"
SelectedItem="{Binding SelectedProduct, Mode=TwoWay}" >
<Custom:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding EditDetailsPageCommand}" />
</i:EventTrigger>
</Custom:Interaction.Triggers>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
on the tap of a product the event command executes...
this.EditDetailsPageCommand = new RelayCommand(this.GotoEditProductDetail, this.CanGotoEditProductDetail);
public void GotoEditProductDetail()
{
//Messenger.Default.Send<NavigateToPageMessage>(new NavigateToPageMessage() { PageName = "SettingsProductDetail", SendObject = DisplayProducts });
// Messenger.Default.Send<NavigateToPageMessage>(new NavigateToPageMessage(){PageName = "SettingsProductDetail", SendObject = SelectedProduct});
Navigator.NavigateTo("SettingsProductDetail", SelectedProduct);
}
It navigates to teh SettingsProductDetail View and in the constructor it errors on this line when setting the DataContext
SettingsProductDetail Xaml
<toolkit:ListPicker HorizontalAlignment="Left" Name="ListPickerCategoryTypes"
ItemsSource="{Binding CategoryTypes}"
Header="Product Types;"
VerticalAlignment="Top" Width="438" Margin="9,6,0,0"
SelectedItem="{Binding SelectedCategoryType, Mode=TwoWay}"
>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryTypeName}" ></TextBlock>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
</toolkit:ListPicker>
public SettingsProductDetail()
{
InitializeComponent();
this.DataContext = new ViewModel.SettingsProductDetailViewModel(Navigator.Object);
}
In my viewmodel for the SettingsProductDetail
I have two properties one for teh Itemsource and one for the selectedItem
public ObservableCollection<CategoryType> CategoryTypes
{
get { return _categoryType; }
set
{
if (value != _categoryType)
{
_categoryType = value;
base.RaisePropertyChanged("CategoryType");
}
}
}
public Model.CategoryType SelectedCategoryType
{
get { return _selectedCategoryType; }
set
{
if (value != _selectedCategoryType)
{
_selectedCategoryType = value;
base.RaisePropertyChanged("SelectedCategoryType");
}
}
}
In the construct is where I am populating the SelectedCategoryType from the object passed from the product view.
public SettingsProductDetailViewModel(object sendObject)
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
ProductDetail = sendObject as DisplayProducts;
if (ProductDetail == null)
{
ProductDetail = new DisplayProducts();
}
else
{
SelectedCategoryType = new CategoryType();
SelectedCategoryType.Id = ProductDetail.FkCategoryTypeID;
SelectedCategoryType.CategoryTypeName = ProductDetail.CategoryTypeName;
}
_TheStoreDataContext = new TheStoreDataContext(ConnectionString);
PopulateHelperObjects();
SettingsProductDetailSaveCommand = new RelayCommand<Model.Product>(param => SaveRecord(), param => (ProductDetail != null));
SettingsProductDetailCancelCommand = new RelayCommand(CancelRecord, () => true);
}
}
Your ViewModel needs to have a property called CategoryTypeSelected which will be of the type T, where T is the type of objects that are in the collection CategoryTypes which you used to bind your ItemsSource. This way, the CategoryTypeSelected will always be the item selected from the list. You bind it like this:
<toolkit:ListPicker HorizontalAlignment="Left" Name="ListPickerCategoryTypes"
ItemsSource="{Binding CategoryTypes, Mode=TwoWay}" ......
SelectedItem="{Binding CategoryTypeSelected, Mode=TwoWay}" >
Of course, your ViewModel needs to implement INotifyPropertyChanged.
Ok I now have it working...
//It was my constructor and how I was setting the property. changed the constructor to this... if (ProductDetail == null)
{
ProductDetail = new DisplayProducts();
}
else
{
SelectedCategoryType = new CategoryType {CategoryTypeName = ProductDetail.CategoryTypeName};
//SelectedCategoryType.Id = ProductDetail.FkCategoryTypeID;
}
// and then changed the property setter to this... set
{
if (_categoryType.Contains(value))
{
_selectedCategoryType = value;
base.RaisePropertyChanged("SelectedCategoryType");
}
else
{
_selectedCategoryType = _categoryType.FirstOrDefault((o) => o.CategoryTypeName == value.CategoryTypeName);
base.RaisePropertyChanged("SelectedCategoryType");
}
//if (value != _selectedCategoryType)
//{
// _selectedCategoryType = value;
// base.RaisePropertyChanged("SelectedCategoryType");
//}
}
I have a XAML UserControl which defines a fairly basic button - I want to define a Bindable property HasMeasurements which will display an overlay image when HasMeasurements is false . However when I include it in my project and bind it to the ViewModel it does not update consistently.
I am sure the ViewModel is properly notifying the bindings since I have simultenously bound the same ViewModel property to another separate element and it updates as expected. Also it works in Blend when I update the mock data.
I have tried this solution which defines a callback where I change the Visibility programatically, however this callback is not called every time the ViewModel property changes, only sometimes. I have also tried binding the Visibility in the XAML using this solution and a non dependency property which also did not work. I have also tried implementing NotifyPropertyChanged out of desperation but no luck there either ...
Here is my XAML,
<UserControl x:Class="MyApp.View.Controls.ConversionBtn"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<Grid x:Name="btnGrid" toolkit:TiltEffect.IsTiltEnabled="True" Height="115">
<Border Background="{StaticResource ImgOverlayColor}" BorderThickness="0" Padding="0" VerticalAlignment="Top" >
<TextBlock x:Name="titleTxtBlock" FontSize="{StaticResource PhoneFontSizeMedium}" Foreground="{StaticResource TileTxtColor}" Margin="6,0,0,0"/>
</Border>
<Image x:Name="notAvailableImg" Source="/Images/ConversionNotAvailableOverlay.png" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="None" />
</Grid>
</Grid>
</UserControl>
Here is the code behind,
// usings here ...
namespace MyApp.View.Controls
{
public partial class ConversionBtn : UserControl
{
public ConversionBtn()
{
InitializeComponent();
if (!TiltEffect.TiltableItems.Contains(typeof(ConversionBtn)))
TiltEffect.TiltableItems.Add(typeof(ConversionBtn));
//this.DataContext = this;
}
public string Title
{
get { return this.titleTxtBlock.Text; }
set { this.titleTxtBlock.Text = value; }
}
public static readonly DependencyProperty HasMeasurementsProperty =
DependencyProperty.Register("HasMeasurements", typeof(bool), typeof(ConversionBtn),
new PropertyMetadata(false, new PropertyChangedCallback(HasMeasurementsPropertyChanged)));
private static void HasMeasurementsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ConversionBtn cBtn = (ConversionBtn)d;
bool val = (bool)e.NewValue;
if (val)
{
cBtn.notAvailableImg.Visibility = Visibility.Collapsed;
}
else
{
cBtn.notAvailableImg.Visibility = Visibility.Visible;
}
cBtn.HasMeasurements = val;
}
public bool HasMeasurements
{
get { return (bool)GetValue(HasMeasurementsProperty); }
set { SetValue(HasMeasurementsProperty, value); }
}
}
}
You have a callback, that is called after HasMeasurment propetry was changed.
And in a callback you change it again. So, you have a logical misstake.
If you need to do something with this value - just save it in private field.
private static void HasMeasurementsPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
ConversionBtncBtn = (ConversionBtn)d;
bool val = (bool)e.NewValue;
if (val)
{
cBtn.notAvailableImg.Visibility = Visibility.Collapsed;
}
else
{
cBtn.notAvailableImg.Visibility = Visibility.Visible;
}
cBtn.SetMeasurments(val);
}
private bool measurmentsState;
public void SetMeasurments(bool value)
{
measurmentsState = value;
}
Here you can get free e-Book by Charls Petzold about Windows Phone Development, there is a nice chapter about Dependency Properties.
Ah damnit, it was a combination of Anton's answer and the fact that I hadn't set my image as 'Content', hence it loaded in Blend but was not present in the deployed app.
So I have a listbox I am trying to make for an "achievement page". Everything works good if I use a List but when I switch it to a List there is nothing displayed, not even from the xaml...
public partial class achievementPage : PhoneApplicationPage
{
public string Description { get; set; }
public string Type { get; set; }
public achievementPage()
{
InitializeComponent();
loadListbox();
}
public achievementPage(string achievementGet, string d1)
{
}
public void loadListbox()
{
achievementStoreage.loadData();
List<achievementPage> achievementList = new List<achievementPage>();
achievementList.Add(new achievementPage(achievementStoreage.achievement1, "This is a test"));
achievementList.Add(new achievementPage(achievementStoreage.achievement2, "This is another test"));
//List<string> achievementList = new List<string>();
//achievementList.Add("Sup");
achievementListBox.ItemsSource = achievementList;
}
}
<ListBox Name="achievementListBox" Margin="0,0,0,0" >
<ListBox.ItemTemplate>
<DataTemplate>
<Button Width="776" Height="120" BorderBrush="Black">
<Button.Content>
<StackPanel Orientation="Horizontal" Height="50">
<StackPanel Orientation="Horizontal" Height="40">
<TextBlock Width="150" Foreground="Black" FontSize="22" Text="Description:" Height="40"/>
</StackPanel>
</StackPanel>
</Button.Content>
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
All I get is a blank page.. P.S don't worry about achievementStoreage, it is working properly.(just where I get stored data)
Frankly, it seems that you are simply not allowed to use ItemTemplates along with UIElements as the items' DataContexts. I've tried with:
achievementListBox.ItemsSource = new [] {"a","b"};
and both items were visible and printed dummy "description" texts, but none of the following lines I've tried has presented anything at all:
achievementListBox.ItemsSource = new [] { new TextBlock(), new TextBlock()};
achievementListBox.ItemsSource = new [] { new Rectangle(), new Rectangle()};
achievementListBox.ItemsSource = new [] { new Grid(), new Grid()};
Trying with your custom page - the same. No items shown.
This is very misleading. The items were shown, but look at the lines above: the controls were created empty, with no contents set!.
It turns out, that if the ListBox detects that the Item is an UIElement, then it does not use the ItemTemplate, but it presents that UIElement directly!
achievementListBox.ItemsSource = new[] { new TextBlock() { Text = "bbb" }, new TextBlock() { Text = "eee" } };
achievementListBox.ItemsSource = new[] { new Rectangle() { Fill = new SolidColorBrush(Colors.Red), Width = 30, Height = 10 }, new Rectangle() { Fill = new SolidColorBrush(Colors.Green), Width = 30, Height = 10 } };
var gridA = new Grid() { Width = 110, Height = 40 }; gridA.Children.Add(new Rectangle() { Fill = new SolidColorBrush(Colors.Red) });
var gridB = new Grid() { Width = 110, Height = 40 }; gridB.Children.Add(new Rectangle() { Fill = new SolidColorBrush(Colors.Green) });
achievementListBox.ItemsSource = new[] { gridA, gridB };
All of the three above examples completely ignore the ListBox.ItemTemplate, and instead, they display two items directly: two textboxes, two rectangles, two larger rectangles (in a Grid).
Getting back to your case: It means that with your original setup, the ListBox would try to display the items directly too - as your custom Page is a UIElement. And indeed it did that! But your pages were ... empty. In the overloaded constructor you omitted the InitializeComponent(); that constructs the View by reading the XAML code. Here is a corrected example that displays a "Hello" three times: Once just because it lies on the page, and next two times because the ListBox rows are set to the same page.
Please excuse me for renaming the classes, I simply started a new project instead of pasting your code.
Please note that I had to add to the XAML some other controls, because the Pages used as the data items would be display as empty, because they would have no items set
public partial class MainPage : PhoneApplicationPage
{
public string Description { get; set; }
public string Type { get; set; }
public MainPage()
{
InitializeComponent();
loadListbox();
}
public MainPage(string achievementGet, string d1)
{
InitializeComponent();
someText.Text = d1;
}
public void loadListbox()
{
achievementListBox.ItemsSource = new[] { new MainPage(null, "ddd"), new MainPage(null, "ccc") };
}
}
<StackPanel>
<TextBlock>
<Run Text="Hello" />
<Run Text=" " />
<Run x:Name="someText" />
</TextBlock>
<ListBox Name="achievementListBox" Margin="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Width="150" Foreground="White"
FontSize="22" Text="This DataTemplate is IGNORED because the Item is UIElement"
Height="40"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
I tried to shape the code in a similar manner to yours, just removed some lines not relevant to the problem. I hope this explains you everything now :)
Oh my. Why do create a list of achievementPages? On your achievementPage you will want to have a ListBox with items of type, like, AchievementItem, CompletedAchievement, NotCompletedAchievement, etc.
Currently, nothing shows up because your code probably throws StackoverflowException (not joking here:)). Look: your achievementPage constructor calls loadListBox that creates two achievementPages and adds them to the list. But creating two achievementPages causes again their constructors to be called two times, which invokes loadListBox two times, and so on..
-- edit: ok, no stackoverflow, I've just noticed the second constructor. You should stick to naming the classes with capital letters you know :) Anyways, putting a Page as a data item of a ListBox on a Page is a bad idea.
What you wanted to get should look more like:
public partial class AchievementPage : PhoneApplicationPage
{
public string Description { get; set; }
public string Type { get; set; }
public AchievementPage()
{
InitializeComponent();
loadListbox();
}
public void loadListbox()
{
var theList = new List<Achievement>();
theList.Add(new Achievement{ AchvCount=3, AchvName="medals" });
theList.Add(new Achievement{ AchvCount=2, AchvName="badges" });
theList.Add(new Achievement{ AchvCount=6, AchvName="zonks" });
achievementListBox.ItemsSource = achievementList;
}
}
public class Achievement : DependencyObject
{
public int AchvCount {get; set;}
public string AchvName {get; set;}
}
<ListBox Name="achievementListBox" Margin="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="You've got:" />
<TextBlock Grid.Column="0" Text="{Binding AchvCount}" />
<TextBlock Grid.Column="0" Text="{Binding AchvName}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'd like to change Foreground color of TextBlock (bellow TitleText and DateText) in DataTemplate from code.
<ListBox x:Name="listBox1" ItemsSource="{Binding}" ScrollViewer.ManipulationMode="Control" SelectionChanged="listBox1_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stackPanel1" HorizontalAlignment="Stretch" Orientation="Horizontal">
<TextBlock FontSize="35" x:Name="TitleText" Text="{Binding Title}" Width="386" Foreground="Black" />
<TextBlock FontSize="25" x:Name="DateText" Text="{Binding Date}" Width="78" Foreground="Black" />
<TextBlock x:Name="Id" Text="{Binding Id}" Visibility="Collapsed" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'd like to do like this in code behind. But It seems not be able to access x:Name property in DataTemplate.
this.TitleText.Foreground = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0);
Does anyone know a good solution for this ?
Why don't you do it the Fast way instead of crawling the Visual Tree.
<TextBlock FontSize="35" Text="{Binding Title}" Width="386" Foreground="[Binding Color}" />
Then all you have to do is:
Add a Color Brush Property in your Collection
Change this property to the color you want
Make sure this property implement INotify or is a Dependency Property
Example
XAML
<Grid>
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">
<TextBlock Text="{Binding Title}" Foreground="{Binding TitleColor}" />
<TextBlock Text="{Binding Date}" Foreground="Black" />
<TextBlock Text="{Binding Id}" Visibility="Collapsed" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
CodeBehind
public partial class MainPage : Page
{
public ObservableCollection<TEST> TestCollection { get; private set; }
public MainWindow()
{
InitializeComponent();
TestCollection = new ObservableCollection<TEST>();
TestCollection.Add(new TEST()
{
TitleColor = Brushes.Black,
ID = 0,
Title = "A",
Date = DateTime.Now,
});
TestCollection.Add(new TEST()
{
TitleColor = Brushes.Red,
ID = 1,
Title = "B",
Date = DateTime.Now.AddDays(1),
});
DataContext = TestCollection;
}
}
public class TEST : INotifyPropertyChanged
{
private Brush _TitleColor;
public Brush TitleColor
{
get
{
return _TitleColor;
}
set
{
_TitleColor = value;
OnPropertyChanged("TitleColor");
}
}
private int _ID;
public int ID
{
get
{
return _ID;
}
set
{
_ID = value;
OnPropertyChanged("ID");
}
}
private string _Title;
public string Title
{
get
{
return _Title;
}
set
{
_Title = value;
OnPropertyChanged("Title");
}
}
private DateTime _Date;
public DateTime Date
{
get
{
return _Date;
}
set
{
_Date = value;
OnPropertyChanged("Date");
}
}
public TEST()
{
}
#region INotifyProperty
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
#endregion
}
You can use FindItem method to find element in visual tree by it name and then change it Foregorund.
((listBox.ItemContainerGenerator.ContainerFromIndex(5) as FrameworkElement).FindName("TitleText") as TextBlock).Foreground = new SolidColorBrush(Color.FromArgb(255, 128, 128, 128));
where 5 is your item index
To Expand on MyKuLLSKI's answer, if your change is based on some value already in your object (eg. an int property that is greater than 5), you could use a ValueConverter (see here for an example) to read the value and return a brush. That is cleaner than adding a colour to your model since it is (arguably) UI related rather than data related.
MyKuLLSKI gave me perfect solution. But I couldn't make it.
I was struggling and I found my problem. I wrote the answer (only for me ) in my blog. Please take a glance it.
Cannot bind two types of data source to one UI target
http://myprogrammingdial.blogspot.com/2012/03/cannot-bind-two-types-of-data-source-to.html