WPF ListBox ItemTemplate Image - wpf

I want to add an Image to ListBox Item.
I have 2 images. One is UpArrow and second one is DownArrow
I am able assign initial image say UpArrow using ItemTemplate and adding Image to it
But Clicking on Sort Button, i want to change the Image. New Image is getting set but not getting changed on the UI.
My Window code is as below
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="Auto" Width="Auto" Loaded="Window_Loaded">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Height="Auto" Width="Auto">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ListBox x:Name="lstBox" ItemsSource="{Binding ListIconDataCollection}" Grid.Row="0" Height="200" Width="200">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:ListIconData}">
<Grid Width="Auto" Height="Auto" Background="Transparent" >
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" Text="{Binding DisplayText}"/>
<Image Grid.Row="0" Grid.Column="1" Source="{Binding Path=ImageSource}" Width="20" HorizontalAlignment="Right"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="btnSort" Grid.Row="1" Height="40" Margin="0,15,0,0" Content="Sort" Click="btnSort_Click"></Button>
</Grid>
My Form Code is below
private void Window_Loaded(object sender, RoutedEventArgs e)
{
lstDataToSet.Add(new ListIconData { DisplayText = "Milind", ItemSortDirection = SortDirection.None, ImageSource = (ImageSource)FindResource("ImgUp") });
lstDataToSet.Add(new ListIconData { DisplayText = "Patil", ItemSortDirection = SortDirection.None });
lstBox.ItemsSource = ListIconDataCollection;
}
This is the form code getting executed but image is not changing on UI
private void btnSort_Click(object sender, RoutedEventArgs e)
{
ListIconData objSelectedItem = (ListIconData)lstBox.SelectedItem;
if (objSelectedItem.ItemSortDirection==SortDirection.None)
{
objSelectedItem.ImageSource = null;
objSelectedItem.ImageSource = (ImageSource)FindResource("ImgDown");
}
}
This is Dictionary1 Resource File Code
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ImageSource x:Key="ImgUp">icons/Up.ico</ImageSource>
<ImageSource x:Key="ImgDown">icons/Down.ico</ImageSource>
Following is ListIconDataClass. This is the list i am binding to the ListBox
public class ListIconData
{
public string DisplayText { get; set; }
public SortDirection ItemSortDirection { get; set; }
public ImageSource ImageSource { get; set; }
}
}

As nikeee13 suggested you should send notification about the changes in your model class
public class ListIconData : INotifyPropertyChanged
{
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public string DisplayText { get; set; }
public SortDirection ItemSortDirection { get; set; }
private ImageSource imgSource;
public ImageSource ImageSource {
get{return imgSource;}
set{
if(imgSource == null || !imgSource.Equals(value))
{
imgSource = value;
OnPropertyChanged("ImageSource");
}
}
}
}

private void btnSort_Click(object sender, RoutedEventArgs e)
{
**lstBox.BeginInit();**
ListIconData objSelectedItem = (ListIconData)lstBox.SelectedItem;
if (objSelectedItem.ItemSortDirection==SortDirection.None)
{
objSelectedItem.ImageSource = null;
objSelectedItem.ImageSource = (ImageSource)FindResource("ImgDown");
}
**lstBox.EndInit();**
}
Please find the code lines in Bold which i added to make it work
Thanks all for your help

Related

Issues in WPF - MVVM - ViewNavigation not working when cliking button in UserControl

I am new to WPF MVVM. I have MainWindow.xaml here Grid is splited into two columns. Column1 hooked with MenuUserControl.xaml which has two button called Page1 and Page2. Column2 hooked with ContentControl which is used to display view based on button click in MenuUserControl.xaml. View navigation not working when I click the button. Please give me a working solution.
I have ViewModel for MainWindowViewModel.cs and MenuuserControlViewModel.cs also Page1UserControlViewModel and Page2UserControlViewModel. MainWindowViewModel contains property called SelectedViewModel which is binded with ContentControl
<ContentControl x:Name="Pages" Content="{Binding SelectedViewModel}"/>
MainWindow.Xaml
<Window x:Class="WpfNavigationViewTestApplication.MainWindow"
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:local="clr-namespace:WpfNavigationViewTestApplication.Views"
xmlns:viewModel="clr-namespace:WpfNavigationViewTestApplication.ViewModel"
mc:Ignorable="d"
Title="Master Part List" Width="1250" Height="700" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<viewModel:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:Page1ViewModel}">
<local: Page1UserControlView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:Page2ViewModel}">
<local: Page2UserControlView/>
</DataTemplate>
</Window.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="125" />
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Row="1" Grid.ColumnSpan="0" HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="10" Background="Gray" ShowsPreview="True" IsEnabled="False" />
<Grid Name="MenuGrid" Grid.Row="1" Grid.Column="0" Margin="3,13,3,3">
<local:MplMenuView DataContext="{Binding Path= MplMenuViewModel}" />
</Grid>
<Grid Name="PageGrid" Grid.Row="1" Grid.Column="1" Margin="13,13,3,3">
<ContentControl x:Name="Pages" Content="{Binding SelectedViewModel}"/>
</Grid>
</Grid>
</Grid>
</ScrollViewer>
</Window>
MainWindowViewModel.cs
public class MainWindowViewModel: INotifyPropertyChanged
{
private BaseViewModel selectedViewModel;
public BaseViewModel SelectedViewModel
{
get { return selectedViewModel; }
set
{
selectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
MenuUserControl.Xaml
<UserControl x:Class="WpfNavigationViewTestApplication.Views. MenuUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfNavigationViewTestApplication.Views"
xmlns:viewModel="clr-namespace:WpfNavigationViewTestApplication.ViewModel"
mc:Ignorable="d"
Height="460" Width="154">
<UserControl.Resources>
<Style x:Key="menuButton" TargetType="Button">
<Setter Property="Height" Value="25"/>
<Setter Property="Width" Value="130"/>
<Setter Property="Margin" Value="0,4,0,0"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</UserControl.Resources>
<UserControl.DataContext>
<viewModel:MenuUserControlViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Vertical">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="105"/>
<RowDefinition Height="90" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" >
<Button Style="{StaticResource menuButton}" Content="Page1" Command="{Binding Page1Command}" />
<Button Style="{StaticResource menuButton}" Content="Page2" Command="{Binding Page2Command}"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Vertical" />
<StackPanel Grid.Row="2" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" >
</StackPanel>
</Grid>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Vertical" >
</StackPanel>
</Grid>
</UserControl>
MenuUserControlViewModel.cs
public class MenuUserControlViewModel : MainWindowViewModel
{
public ICommand Page1Command { get; set; }
public ICommand Page2Command { get; set; }
public MenuUserControlViewModel ()
{
Page1Command = new BaseCommand(OpenPage1View);
Page2Command = new BaseCommand(OpenPage2View);
}
private void OpenPage1View (object obj)
{
SelectedViewModel = new Page1UserControlViewModel ();
}
private void OpenPage2View (object obj)
{
SelectedViewModel = new Page2UserControlViewModel ();
}
}
BaseCommand.cs
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged;
public BaseCommand(Action<object> method)
: this(method, null)
{
}
public BaseCommand(Action<object> method, Predicate<object> canExecute)
{
_method = method;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}
You are setting the SelectedViewModel property of another instance of MainWindowViewModel. MenuUserControlViewModel shouldn't inherit from MainWindowViewModel. It should communicate with the existing one.
Get rid of this from MenuUserControl.xaml:
<UserControl.DataContext>
<viewModel:MenuUserControlViewModel/>
</UserControl.DataContext>
...and create an instance of the MenuUserControlViewModel in the MainWindowViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private BaseViewModel selectedViewModel;
public BaseViewModel SelectedViewModel
{
get { return selectedViewModel; }
set
{
selectedViewModel = value;
OnPropertyChanged("SelectedViewModel");
}
}
public MainWindowViewModel()
{
MplMenuViewModel = new MenuUserControlViewModel(this);
}
public MenuUserControlViewModel MplMenuViewModel { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class MenuUserControlViewModel : MainWindowViewModel
{
private readonly MainWindowViewModel _mainWindowViewModel;
public ICommand Page1Command { get; set; }
public ICommand Page2Command { get; set; }
public MenuUserControlViewModel(MainWindowViewModel mainWindowViewModel)
{
_mainWindowViewModel = mainWindowViewModel;
Page1Command = new BaseCommand(OpenPage1View);
Page2Command = new BaseCommand(OpenPage2View);
}
private void OpenPage1View(object obj)
{
_mainWindowViewModel.SelectedViewModel = new Page1UserControlViewModel();
}
private void OpenPage2View(object obj)
{
_mainWindowViewModel.SelectedViewModel = new Page2UserControlViewModel();
}
}
MainWindow.xaml:
<local:MplMenuView DataContext="{Binding Path=MplMenuViewModel}" />

How I can print listbox item count along with Expander header?

I want to print listbox item count alog with header of expander like
header1 (count) . How can I achieve this in WPF
You haven't provided any code for what you have done, such as if you are using a ViewModel or doing code-behind. There are multiple ways to go about it.
My ViewModel Way
In this example, I've made a ViewModel containing an ObservableCollection of string to populate the ListBox. The Expander header is bound to a property that is populated using a combination of the HeaderText and ItemCount.
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _headerText = string.Empty;
private string _headerTextFull = string.Empty;
private ObservableCollection<string> _listItems = new ObservableCollection<string>();
private int _itemCount = 0;
public ViewModel() { }
public string HeaderText
{
get { return _headerText; }
set
{
_headerText = value;
NotifyPropertyChanged("HeaderText");
UpdateHeader();
}
}
public string HeaderTextFull
{
get { return _headerTextFull; }
set
{
_headerTextFull = value;
NotifyPropertyChanged("HeaderTextFull");
}
}
public ObservableCollection<string> ListItems
{
get { return _listItems; }
set
{
_listItems = value;
NotifyPropertyChanged("ListItems");
ItemCount = (_listItems != null ? _listItems.Count : 0);
}
}
public int ItemCount
{
get { return _itemCount; }
set
{
_itemCount = value;
NotifyPropertyChanged("ItemCount");
UpdateHeader();
}
}
private void UpdateHeader()
{
HeaderTextFull = String.Format("{0} ({1})", _headerText, _itemCount);
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The XAML for the Window:
<Window x:Class="SO37192142.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Expander Grid.Row="0" Name="expander1" Header="{Binding Path=HeaderTextFull, FallbackValue='Items'}" IsExpanded="True">
<Grid>
<ListBox Name="listBox1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<Button Grid.Row="1" Content="Add an Item" Click="Button_Click" />
</Grid>
</Window>
The code-behind:
public partial class Window1 : Window
{
ViewModel myModel = new ViewModel();
public Window1()
{
InitializeComponent();
myModel.ListItems.CollectionChanged += new NotifyCollectionChangedEventHandler(ListItems_CollectionChanged);
myModel.HeaderText = "Items";
myModel.ListItems.Add("Item 1");
myModel.ListItems.Add("Item 2");
this.DataContext = myModel;
}
void ListItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
myModel.ItemCount = myModel.ListItems.Count;
}
void Button_Click(object sender, RoutedEventArgs e)
{
myModel.ListItems.Add("Another item");
}
}
When it starts, the Expander header will say "Items (2)". Every time the button is clicked, the header will update to show the new count.
Slight Variation of Above
Here's an example that provides the above example, but also adds a second list to demonstrate a different way. Notice the Expander.Header section.
<Window x:Class="SO37192142.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Expander Grid.Row="0" Grid.Column="0" Name="expander1" Header="{Binding Path=HeaderTextFull, FallbackValue='Items'}" IsExpanded="True">
<Grid>
<ListBox Name="listBox1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<!-- SECOND EXPANDER THAT DOESN'T RELY ON A VIEWMODEL -->
<Expander Grid.Row="0" Grid.Column="1" Name="expander2" IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ElementName=listBox2, Path=Items.Count, UpdateSourceTrigger=PropertyChanged, StringFormat={}Items ({0})}" />
</StackPanel>
</Expander.Header>
<Grid>
<ListBox Name="listBox2" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<Button Grid.Row="1" Grid.ColumnSpan="2" Content="Add an Item" Click="Button_Click" />
</Grid>
</Window>
Only Code Behind
If, for some reason, you are just using code-behind, you can do it this way:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
((INotifyCollectionChanged)listBox1.Items).CollectionChanged += new NotifyCollectionChangedEventHandler(ListBox_CollectionChanged);
}
void ListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
expander1.Header = "Items (" + listBox1.Items.Count + ")";
}
}

Binding List<string> property to Listbox WPF

Can someone help me? I have the folowing XAML code in my MainWindow.xaml file:
<ListBox ItemsSource="{Binding Files}" HorizontalAlignment="Left"
Height="371" Margin="281,53,0,0" VerticalAlignment="Top"
Width="609">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in my ViewModel.cs I have property:
public List<string> Files { get; set; }
But, when I click the button and add some items to Files nothing happens.
P.S. Sorry for my bad English :)
List does not implement INotifyCollectionChanged, instead of List, use ObservableCollection<string>
Additional Info: List vs ObservableCollection vs INotifyPropertyChanged
Here is your solution, just add this code and press 'Add String' button to make it work. I have used 'ObservableCollection' instead of List and made to listen it using 'INotifyPropertyChanged' interface in ViewModel.cs class
MainWindow.xaml
<Window x:Class="ListBox_Strings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myApp="clr-namespace:ListBox_Strings"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<myApp:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Files}" HorizontalAlignment="Left"
VerticalAlignment="Top" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="Add String" Click="Button_Click"></Button>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace ListBox_Strings
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as ViewModel;
vm.Files.Add("New String");
}
}
}
ViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace ListBox_Strings
{
public class ViewModel:INotifyPropertyChanged
{
private ObservableCollection<string> m_Files;
public ObservableCollection<string> Files
{
get { return m_Files; }
set { m_Files = value;
OnPropertyChanged("Files"); }
}
public ViewModel()
{
Files = new ObservableCollection<string>();
Files.Add("A");
Files.Add("B");
Files.Add("C");
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}

wpf listbox resetting selectedIndex when containing grid resizes

I need a composite view consisting of a top part that can be hidden, a bottom part consisting of a navigation menu (listbox) on the left and a content area on the right.
One option in the listbox should make the top-part collapsed and other listoptions should show the top-part.
My idea was to split the screen in top- and bottom part using a grid with three rows (middle row for gridsplitter) and currently Im using codebehind for changing the height of the top grid.
The Problem is that when I set the Grid.Height-prop from codebehind, my ListBox.SelectedIndex is set from 1 to 0 ! Thing is that if I change order of the two menu-items, it works fine!
We are using Caliburn Micro, but I don't think that changes anything - the example could be implemented with INotifyPropertyChanged as well.
<UserControl x:Uid="UserControl_1" x:Class="VisionAir.Client.Gui.Common.Windows.Foo.Views.BarView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="300" Width="300">
<UserControl.Resources>
<ResourceDictionary>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<Grid Name="ResizableGrid">
<Grid.RowDefinitions>
<RowDefinition Height="100" Name="TopRow"/>
<RowDefinition Height="10" Name="SplitterRow"/>
<RowDefinition Name="BottomRow"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Name="TopRowGrid" Background="Chartreuse" Visibility="{Binding ShowSearchView, Converter={StaticResource BoolToVisConverter}}"/>
<GridSplitter Grid.Row="1" Visibility="{Binding ShowSearchView,Converter={StaticResource BoolToVisConverter}}" ResizeBehavior="PreviousAndCurrent" HorizontalAlignment="Stretch" Height="10"/>
<Grid Name="ListGrid" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding Items}" SelectedIndex="{Binding SelectedIndex}" DisplayMemberPath="DisplayName">
</ListBox>
<Grid Background="Chocolate" Grid.Column="1"/>
</Grid>
<Canvas Name="FooCanvas" Visibility="{Binding ShowSearchView, Converter={StaticResource BoolToVisConverter}}"></Canvas>
</Grid>
</UserControl>
using System.Windows;
using System.Windows.Controls;
namespace VisionAir.Client.Gui.Common.Windows.Foo.Views
{
/// <summary>
/// Interaction logic for BarView.xaml
/// </summary>
public partial class BarView : UserControl
{
public BarView()
{
InitializeComponent();
FooCanvas.IsVisibleChanged += FooCanvas_IsVisibleChanged;
}
void FooCanvas_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
bool isVisible = (bool)e.NewValue;
if (isVisible)
{
TopRow.Height = new GridLength(100);
SplitterRow.Height = new GridLength(0, GridUnitType.Auto);
}
else
{
TopRow.Height = new GridLength(0, GridUnitType.Auto);
SplitterRow.Height = new GridLength(0);
}
}
}
}
using System.Collections.Generic;
using Caliburn.Micro;
namespace VisionAir.Client.Gui.Common.Windows.Foo.ViewModels
{
public class BarViewModel : PropertyChangedBase
{
private List<Screen> _items = new List<Screen>();
public BarViewModel()
{
Items.Add(new Screen() { DisplayName = "Hide" });
Items.Add(new Screen() { DisplayName = "Show" });
}
public bool ShowSearchView { get { return SelectedIndex == 1; } }
private int _selectedIndex;
public List<Screen> Items
{
get { return _items; }
set { _items = value; }
}
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
System.Diagnostics.Debug.WriteLine(value);
_selectedIndex = value;
NotifyOfPropertyChange(() => ShowSearchView);
}
}
}
}

Trouble with DataBinding to a ListBox in WP7 which is not displaying the bound content

I have a DataBinding on a ListBox, bound to an ObservableCollection. Debugging at runtime shows the ObservableCollection does have items in it, and they're not null. My code all looks fine, however for some reason nothing is being displayed in my ListBox. It definitely was working previously, however it no longer is - and I can't figure out why. I've examined previous versions of the code and found no differences that would have any effect on this - minor things like Width="Auto" etc.
I based my code off of the example found here:
http://msdn.microsoft.com/en-us/library/hh202876.aspx
So, my code:
XAML:
<phone:PhoneApplicationPage
x:Class="MyNamespace.MyItemsListPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="PageTitle" Text="MyPageTitle" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<!-- Bind the list box to the observable collection. -->
<ListBox x:Name="myItemsListBox" ItemsSource="{Binding MyItemsList}" Margin="12, 0, 12, 0" Width="440">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" Width="440">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding MyItemNumber}"
FontSize="{StaticResource PhoneFontSizeLarge}"
Grid.Column="0"
VerticalAlignment="Center"
Margin="0,10"
Tap="TextBlock_Tap"/>
<TextBlock
Text="{Binding MyItemName}"
FontSize="{StaticResource PhoneFontSizeLarge}"
Grid.Column="1"
VerticalAlignment="Center"
Margin="0,10"
Tap="TextBlock_Tap" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
C#:
namespace MyNamespace
{
public partial class MyItemsListPage : PhoneApplicationPage, INotifyPropertyChanged
{
private static ObservableCollection<MyItem> _myItemsList;
private ObservableCollection<MyItem> MyItemsList
{
get
{
return _myItemsList;
}
set
{
if (_myItemsList!= value)
{
_myItemsList= value;
NotifyPropertyChanged("MyItemsList");
}
}
}
public MyItemsListPage ()
{
InitializeComponent();
this.DataContext = this;
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
HelperClass helper = new HelperClass();
MyItemsList = helper.GetItems(this.NavigationContext.QueryString["query"]);
base.OnNavigatedTo(e); // Breakpoint here shows "MyItemsList" has MyItem objects in it.
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// Used to notify Silverlight that a property has changed.
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
The Helper class is a connector to my read-only local database on the device. It returns an ObservableCollection<MyItem>:
public ObservableCollection<MyItem> GetItems(string itemName)
{
// Input validation etc.
// Selecting all items for testing
var itemsInDB =
from MyItem item in db.Items
select item;
return new ObservableCollection<MyItem>(itemsInDB);
}
And finally the MyItem class:
[Table]
public class MyItem: INotifyPropertyChanged, INotifyPropertyChanging
{
private int _myItemId;
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int MyItemId
{
...
}
private string _myItemName;
[Column(CanBeNull = false)]
public string MyItemName
{
get
{
return _myItemName;
}
set
{
if (_myItemName!= value)
{
NotifyPropertyChanging("MyItemName");
_myItemName= value;
NotifyPropertyChanged("MyItemName");
}
}
}
private int _myItemNumber;
[Column]
public int MyItemNumber
{
get
{
return _myItemNumber;
}
set
{
if (_myItemNumber!= value)
{
NotifyPropertyChanging("MyItemNumber");
_myItemNumber= value;
NotifyPropertyChanged("MyItemNumber");
}
}
}
// Other properties, NotifyPropertyChanged method, etc...
}
This is rather frustrating as my DataBinding elsewhere in the application is working perfectly, so I've no idea why I can't get this to work.
The problem was that my ObservableCollection was private. Changing it to have a public access modifier allowed my ListBox to display the contents:
public ObservableCollection<MyItem> MyItemsList
Simply that you're binding to properties named incorrectly:
Text="{Binding ItemName}" should be Text="{Binding MyItemName}"
Notice you left out "My"

Resources