i have been successfully adding an item to list in a MVVM, and now my problem is maintaining the list in the view model. Every time i navigate to a page or go back to a page and return to that listview, the list resets. how will i able to achieve that? i am currently using the prism to build the MVVM.
The ViewModel:
public ObservableCollection<CartData> _cartData;
public ObservableCollection<CartData> CartData
{
get {
return _cartData;
}
set {
SetProperty(ref _cartData, value);
}
}
private DelegateCommand _addItemCommand;
public ICommand AddItemCommand
{
get
{
if (_addItemCommand == null)
{
_addItemCommand = new DelegateCommand(AddToCart);
}
return _addItemCommand;
}
}
public void AddToCart() {
CartData.Add(new CartData { Cakename = "Black Forest", Cakeprice = 104 });
}
View:
.....
<Page.DataContext>
<vm:CartingDataSource/>
</Page.DataContext>
....
<ListView
x:Name="itemListView"
AutomationProperties.AutomationId="ItemsListView"
AutomationProperties.Name="Items"
TabIndex="1"
Margin="-10,130,0,264"
Padding="120,0,0,60"
ItemsSource="{Binding cartData}"
IsSwipeEnabled="False" Grid.RowSpan="2" ItemClick="itemListView_ItemClick" SelectionChanged="itemListView_SelectionChanged_1" IsItemClickEnabled="True">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="60" Height="60">
<Image Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Source="Assets/wewewew.jpg"/>
</Border>
<StackPanel Grid.Column="1" Margin="10,0,0,0">
<TextBlock Text="{Binding Cakename}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" MaxHeight="40"/>
<TextBlock Text="{Binding Cakeprice}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="FrameworkElement">
<Setter Property="Margin" Value="0,0,0,10"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
Here if your ViewModel is CartingDataSource in that case it is being instatiated on every page load. Now if that is the case, then you are creating a new instance of your collection in your constructor as below:
public CartingDataSource() {
CartData = new ObservableCollection<CartData>();
}
As a result of which it re-initializes your collection.
You need to remove the initialization from your constructor and do something like this:
public ObservableCollection<CartData> _cartData;
public ObservableCollection<CartData> cartData
{
get {
if(_cartData == null)
{
_cartData = new ObservableCollection<CartData>();
}
return _cartData;
}
set {
_cartData = value;
}
}
Related
i'm trying to code a POS system. i want to have two list, one with articles and when user click on an article the article is added to the second list and the list reflesh automatically.
here is my code
using Newtonsoft.Json;
public class ArticlesDetailsViewModel : INotifyPropertyChanged, IArticleViewModel
{
public ArticlesDetailsViewModel()
{
string baseUrl = "http://api.market.csa.com/api/v1.1/Article/ListArticle";
bool dr;
using (HttpClient client = new HttpClient())
{
var response = client.GetAsync(baseUrl);
var result = response.Result;
dr = result.IsSuccessStatusCode;
if (dr)
{
string jsonstring = result.Content.ReadAsStringAsync().Result;
var articles = JsonConvert.DeserializeObject<ListArticleResponse>(jsonstring);
foreach(var art in articles.data)
{
ListArticle.Add(art);
}
}
else
{
MessageBox.Show("Une erreur s'est produite lors de la recuration des articles, vérifiez votre connexion internet", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
//intializing ICollectionView using collection(ObservableCollection)
ArticleCollection = CollectionViewSource.GetDefaultView(ListArticle);
CommandeCollection = CollectionViewSource.GetDefaultView(ListCommande);
ArticleCollection.Filter = Filter;
}
private ObservableCollection<ArticleModel> _ListArticle = new ObservableCollection<ArticleModel>();
public ObservableCollection<ArticleModel> ListArticle
{
get { return _ListArticle; }
set { _ListArticle= value;
NotifyPropertyChanged("ListArticle");
}
}
private ICollectionView _ArticleCollection;
public ICollectionView ArticleCollection
{
get { return _ArticleCollection; }
set { _ArticleCollection= value;
NotifyPropertyChanged("ArticleCollection");
}
}
private ICollectionView _CommandeCollection;
public ICollectionView CommandeCollection
{
get { return _CommandeCollection; }
set { _CommandeCollection=value;
NotifyPropertyChanged("CommandeCollection");
}
}
private bool Filter(object emp)
{
ArticleModel employee = emp as ArticleModel;
//you can write logic for filter here
if (!string.IsNullOrEmpty(ArticleName) && !string.IsNullOrEmpty(ArticleCode))
return employee.NomArticle.Contains(ArticleName) && employee.NomArticle.Contains(ArticleName);
else if (string.IsNullOrEmpty(ArticleName))
return employee.Code.Contains(ArticleCode);
else
return employee.NomArticle.Contains(ArticleName);
}
private string _articleName = string.Empty;
public string ArticleName
{
get { return _articleName;
}
set
{
_articleName=value;
NotifyPropertyChanged("ArticleName");
ArticleCollection.Refresh();
}
}
private string _articleCode = string.Empty;
public string ArticleCode
{
get { return _articleCode; }
set
{
_articleCode= value;
NotifyPropertyChanged("ArticleCode");
ArticleCollection.Refresh();
}
}
private ArticleModel _ArticleSelected = new ArticleModel();
public ArticleModel ArticleSelected
{
get {
return _ArticleSelected; }
set
{
_ArticleSelected=value;
NotifyPropertyChanged("ArticleSelected");
}
}
private ObservableCollection<ArticleModel> _ListCommande = new ObservableCollection<ArticleModel>();
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
PropertyChanged(this, new PropertyChangedEventArgs("CommandeCollection"));
}
}
public ObservableCollection<ArticleModel> ListCommande
{
get
{
return _ListCommande;
}
set
{
_ListCommande= value;
NotifyPropertyChanged("ListCommande");
}
}
}
}
the xaml for the commande list:
<UserControl
x:Class="WantaMarketDesktop.View.CommandesListingView"
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:local="clr-namespace:WantaMarketDesktop.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<ListView ScrollViewer.CanContentScroll="True" ScrollViewer.IsDeferredScrollingEnabled="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ItemsSource="{Binding CommandeCollection,UpdateSourceTrigger=PropertyChanged}" Grid.ColumnSpan="2" >
<ListView.View>
<GridView AllowsColumnReorder="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.CanContentScroll="True" >
<GridView.Columns>
<GridViewColumn Header=" ID" DisplayMemberBinding="{Binding IdArticle}"/>
<GridViewColumn Header="Nom" DisplayMemberBinding="{Binding NomArticle}"/>
<GridViewColumn Header="Quantite" DisplayMemberBinding="{Binding Quantite}"/>
<GridViewColumn Header="Prix" DisplayMemberBinding="{Binding Prix}"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<Label Grid.Row="1" Content="Montant total de la commande : " FontSize="13"/>
<Label Grid.Row="1" Grid.Column="1" FontWeight="SemiBold" Content="2750 XFA" FontSize="17" Width="auto" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontStyle="Normal" />
</Grid>
</UserControl>
the xaml article list
<UserControl x:Class="WantaMarketDesktop.View.ArticlesDetails"
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:WantaMarketDesktop.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="0,10,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="5">
<TextBlock Text="Code : " VerticalAlignment="Center" Margin="10,5,5,5"/>
<TextBox Text="{Binding ArticleCode,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Width="200"/>
<TextBlock Text="Nom : " VerticalAlignment="Center" Margin="10,5,5,5"/>
<TextBox Text="{Binding ArticleName,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Width="200"/>
</StackPanel>
<StackPanel Grid.Row="1" Margin="10,0">
<Separator HorizontalAlignment="Stretch" Height="5" Margin="0,5,0,10"/>
<!--setting ItemsSource of ListView to ICollectionView-->
<ListView Cursor="Hand" ItemsSource="{Binding ArticleCollection}" SelectedItem="{Binding ArticleSelected}" HorizontalAlignment="Stretch" VerticalAlignment="Top" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Image ToolTip="{Binding Carracteristique}" Source="https://phonesdata.com/files/models/Xiaomi-Redmi-Note-10S-298.jpg" HorizontalAlignment="Stretch" VerticalAlignment="Top" Stretch="UniformToFill" MinHeight="50" MaxHeight="100" />
<TextBlock Text="{Binding NomArticle}" ToolTip="{Binding NomArticle}" HorizontalAlignment="Stretch" FontSize="12" FontFamily="Franklin Gothic Medium" VerticalAlignment="Bottom" />
<WrapPanel Orientation="Horizontal" Margin="0,5,0,0">
<TextBlock Text="{Binding Prix}" HorizontalAlignment="Right" FontSize="11" FontFamily="Franklin Gothic Medium" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding Code}" Margin="60,0,0,0" HorizontalAlignment="Stretch" FontSize="9" FontFamily="Franklin Gothic Medium" VerticalAlignment="Center" />
</WrapPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
</UserControl>
and the Interface
public interface IArticleViewModel
{
ObservableCollection<ArticleModel> ListArticle { get; set; }
ObservableCollection<ArticleModel> ListCommande { get; set; }
}
for now i have this
i want to do that when we click on article image , the article add to the list at the left
It is esssential that all controls that depend on the same data (DataContext) must share the same instance of the view model class (DataContext).
Normally you don't set the DataContext explicitly. You use DataContext inheritance where all child elements implicitly inherit their DataContext from their parent element.
Add ArticlesDetailsViewModel as a public property to your MainViewModel. Set the MainViewModel as the DataContext of the MainWindow. Then let the common parent of both UserControl elements bind its DataContext to the ArticlesDetailsViewModel property of the MainViewModel (current DataContext).
Don't forget to remove the DataContext assignment from all UserControl constructors.
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged
{
// Shared instance
public IArticleViewModel ArticlesDetailsViewModel { get; }
public MainViewModel => this.ArticlesDetailsViewModel = new ArticlesDetailsViewModel();
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponenet();
this.DataContext = new MainViewModel();
}
}
MainWindow.xaml
<!-- DataContext is 'MainViewModel' ==> explcitly set -->
<Window>
<!-- DataContext is 'MainViewModel' ==> inherited -->
<StackPanel>
<!-- DataContext is 'MainViewModel' ==> inherited -->
<Button />
<!-- DataContext is 'MainViewModel' ==> inherited -->
<ListBox />
<!-- DataContext is 'ArticlesDetailsViewModel' ==> explcitly set -->
<StackPanel DataContext="{Binding ArticlesDetailsViewModel}">
<!-- DataContext is 'ArticlesDetailsViewModel' ==> inherited -->
<CommandesListingView />
<!-- DataContext is 'ArticlesDetailsViewModel' ==> inherited -->
<ArticlesDetails />
</StackPanel>
</StackPanel>
</Window>
I am using Prism and WPF. Here is my ViewModel:
using DialogueTool.Service.Abstracts;
using DialogueTool.Service.Models.Concretes;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace DialogueTool.UI.ViewModels
{
public class DialogueEntryControlViewModel : BindableBase, INavigationAware
{
private IServices services;
private DialogueWrapperModel dialogueWrapperModel;
public DialogueEntryControlViewModel(IServices _services)
{
services = _services;
}
DialogueWrapperModel DialogueWrapperModel { get; set; }
public void OnNavigatedTo(NavigationContext navigationContext)
{
dialogueWrapperModel = navigationContext.Parameters.GetValue<DialogueWrapperModel>("DialogueWrapperModel");
DialogueEntries = new ObservableCollection<DialogueEntryModel>(dialogueWrapperModel.DialogueEntries);
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
//throw new NotImplementedException();
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
//throw new NotImplementedException();
}
#region Properties
private ObservableCollection<DialogueEntryModel> _breadCrumbs = new ObservableCollection<DialogueEntryModel>();
public ObservableCollection<DialogueEntryModel> BreadCrumbs
{
get { return _breadCrumbs; }
set { SetProperty(ref _breadCrumbs, value); }
}
/// <summary>
///
/// </summary>
public ObservableCollection<DialogueEntryModel> _dialogueEntries = new ObservableCollection<DialogueEntryModel>();
public ObservableCollection<DialogueEntryModel> DialogueEntries
{
get { return _dialogueEntries; }
set{ SetProperty(ref _dialogueEntries, value); }
}
private DialogueEntryModel _selectedDialogueEntry;
public DialogueEntryModel SelectedDialogueEntry
{
get { return _selectedDialogueEntry; }
set { SetProperty(ref _selectedDialogueEntry, value); }
}
private DialogueEntryModel _selectedDialogueEntryChoice;
public DialogueEntryModel SelectedDialogueEntryChoice
{
get { return _selectedDialogueEntryChoice; }
set {
SetProperty(ref _selectedDialogueEntryChoice, value);
BreadCrumbs.Add(value);
}
}
#endregion
#region Commands
#endregion
}
}
Here is my xaml:
<UserControl x:Class="DialogueTool.UI.Views.Controls.DialogueEntryControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:templateSelector="clr-namespace:DialogueTool.UI.DataTemplateSelectors">
<UserControl.Resources>
<DataTemplate x:Key="QuestionDataTemplateTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
TextWrapping="Wrap"
VerticalAlignment="Center"
Text="{Binding Text, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="0"
FontSize="25"
FontWeight="Bold"
VerticalAlignment="Center">⇩</TextBlock>
<TreeView Grid.ColumnSpan="2"
Grid.Column="0"
Grid.Row="1"
Visibility="{Binding Path=DialogueEntries, Converter={StaticResource EmptyListToVisibilityConverter} }">
<TreeViewItem Header="Choices"
IsExpanded="{Binding Path=DialogueEntries, Converter={StaticResource EmptyListToBoolConverter} }">
<StackPanel Width="160">
<!--<Label>Search</Label>
<TextBox Text="{Binding ChapterSearchText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />-->
<!---->
<Label>Choices</Label>
<ListBox ItemsSource="{Binding DialogueEntries}"
SelectedItem="{Binding Path=SelectedDialogueEntryChoice, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="146">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
MaxWidth="150"
TextWrapping="Wrap"
VerticalAlignment="Center"
Text="{Binding Text, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="0"
FontSize="25"
FontWeight="Bold"
VerticalAlignment="Center">⇨</TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</TreeViewItem>
</TreeView>
</Grid>
</DataTemplate>
<DataTemplate x:Key="TextDataTemplateTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
TextWrapping="Wrap"
VerticalAlignment="Center"
Text="{Binding Text, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<TextBlock Grid.Column="1"
Grid.Row="0"
FontSize="25"
FontWeight="Bold"
VerticalAlignment="Center">⇩</TextBlock>
</Grid>
</DataTemplate>
<templateSelector:DialogueEntryDataTemplateSelector x:Key="DialogueEntryDataTemplateSelector"
x:Name="DialogueEntryDataTemplateSelector"
TextTemplate="{StaticResource TextDataTemplateTemplate}"
QuestionTemplate="{StaticResource QuestionDataTemplateTemplate}" />
</UserControl.Resources>
<StackPanel>
<StackPanel FlowDirection="LeftToRight"
Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding BreadCrumbs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Excerpt}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!---->
<Label>Dialogue Entries</Label>
<ListBox ItemsSource="{Binding DialogueEntries}"
SelectedItem="{Binding SelectedDialogueEntry, Mode=TwoWay}"
ItemTemplateSelector="{StaticResource DialogueEntryDataTemplateSelector}">
</ListBox>
</StackPanel>
</UserControl>
The following binding to SelectedDialogueEntryChoice is not triggering my ViewModel's SelectedDialogueEntryChoice property:
<ListBox ItemsSource="{Binding DialogueEntries}"
SelectedItem="{Binding Path=SelectedDialogueEntryChoice, Mode=TwoWay}">
I've read that it may be that the Tree is absorbing the click event but I haven't yet figured out how to ensure the selected item event works
Just to ensure I don't miss anything off that may be of importance, here is the DataTemplateSelector class I am using:
using DialogueTool.Domain.Enums;
using DialogueTool.Service.Models.Concretes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace DialogueTool.UI.DataTemplateSelectors
{
public class DialogueEntryDataTemplateSelector : DataTemplateSelector
{
public DataTemplate QuestionTemplate { get; set; }
public DataTemplate TextTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var dialogueEntryModel = item as DialogueEntryModel;
switch (dialogueEntryModel.Type)
{
case DialogueEntryType.Question:
return QuestionTemplate;
case DialogueEntryType.Text:
return TextTemplate;
default:
return TextTemplate;
}
}
}
}
Responses to questions in comments:
Q: What does "does not trigger the property" mean?
A: The following set method is not triggered when the selected item is changed.
private DialogueEntryModel _selectedDialogueEntryChoice;
public DialogueEntryModel SelectedDialogueEntryChoice
{
get { return _selectedDialogueEntryChoice; }
set {
SetProperty(ref _selectedDialogueEntryChoice, value);
BreadCrumbs.Add(value);
}
}
As an example, the SelectedDialogueEntry set method does get fired
Things i'm trying:
I have tried to Bind to the UserControl element of my Xaml which I was hoping has access to the DataContext and thus my ViewModel... But to no avail:
<ListBox ItemsSource="{Binding DialogueEntries}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedDialogueEntryChoice}">
Things i've found out:
If I take the code out of the template selector, the SelectedDialogueEntryChoice works. So I am assuming it's to do with it losing access to the ViewModel
The trick was to bind directly to DataContext. I also needed to find AncestorType of UserControl which the DataContext is bound to:
<ListBox ItemsSource="{Binding DialogueEntries}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedDialogueEntryChoice, Mode=TwoWay}">
OK, I think similar questions have already been asked, but I can't get this to work. I have a View with an ItemsControl like this:
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Content="Model Health Report:" Margin="10,10,10,0" Height="26" VerticalAlignment="Top" FontWeight="Bold"/>
<ItemsControl Grid.Row="1"
ItemsSource="{Binding HealthReports, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type hr:HealthReportSummaryControl}"/>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
That has a view model behind it like this:
public class CommunicatorViewModel : ViewModelBase
{
public ObservableCollection<HealthReportSummaryViewModel> HealthReports { get; set; }
public CommunicatorModel Model { get; set; }
public CommunicatorViewModel(HealthReportData data)
{
Model = new CommunicatorModel();
HealthReports = new ObservableCollection<HealthReportSummaryViewModel>
{
new HealthReportSummaryViewModel {Title = "View1", Description = "Some desc."},
new HealthReportSummaryViewModel {Title = "View2", Description = "Some desc."}
};
}
}
As you can see I am binding it to an ObservableCollection of HealthReportSummaryViewModel objects. These are populated in the constructor. I checked the objects at runtime, they are correct.
The actual control looks like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="2"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0"
Fill="{Binding FillColor}"
Margin="2"/>
<Rectangle Grid.Column="1"
Fill="DarkGray"
Margin="0,2"/>
<Label Content="{Binding Title}"
Grid.Column="2"
Margin="5,0,10,0"
VerticalAlignment="Top"/>
<TextBlock Grid.Column="2"
Margin="5,10,10,0"
TextWrapping="Wrap"
Text="{Binding Description}"/>
</Grid>
With a view model:
public class HealthReportSummaryViewModel : ViewModelBase
{
private System.Windows.Media.Color _fillColor;
public System.Windows.Media.Color FillColor {
get { return _fillColor; }
set { _fillColor = value; RaisePropertyChanged(() => FillColor); }
}
private string _title;
public string Title
{
get { return _title; }
set { _title = value; RaisePropertyChanged(() => Title); }
}
private string _description;
public string Description
{
get { return _description; }
set { _description = value; RaisePropertyChanged(() => Description); }
}
}
I am getting no exceptions, but my window has only empty items. There is a rectangle in the user control that is not dependent on data binding so perhaps this is an issue with the size of the content? I can't figure this out. It's all blank. Do I need to somehow set the size for each ItemsControl item, or will they just adjust to size of the grid they are placed in? What am i missing here? All help will be appreciated.
Your DataTemplate definition is wrong:
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type hr:HealthReportSummaryControl}"/>
</ItemsControl.ItemTemplate>
This defines an empty data template for the items of the HealthReportSummaryControl type.
Instead, you should define it like that:
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type hr:HealthReportSummaryViewModel}">
<hr:HealthReportSummaryControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
This defines a template for the HealthReportSummaryViewModel items.
I'm in search of some help. I've created a very basic MVVM setup. My object is called VNode which has the properties Name,Age,Kids. What I want to happen is when the user selects VNodes on the left, it displays their more in depth data on the right as scene in the image below. I'm not sure how to go about doing this.
image 1: Current
Image 2: Goal
If you don't feel like using the code below to recreate the window you can grab the project solution files from here: DropboxFiles
VNode.cs
namespace WpfApplication1
{
public class VNode
{
public string Name { get; set; }
public int Age { get; set; }
public int Kids { get; set; }
}
}
MainWindow.xaml
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="8" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" Background="AliceBlue" ItemsSource="{Binding VNodes}" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<ListBox Grid.Column="2" Background="LightBlue" ItemsSource="{Binding VNodes}">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text=":" FontWeight="Bold" />
<TextBlock Text=" age:"/>
<TextBlock Text="{Binding Age}" FontWeight="Bold" />
<TextBlock Text=" kids:"/>
<TextBlock Text="{Binding Kids}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApplication1
{
public class MainViewModel : ObservableObject
{
private ObservableCollection<VNode> _vnodes;
public ObservableCollection<VNode> VNodes
{
get { return _vnodes; }
set
{
_vnodes = value;
NotifyPropertyChanged("VNodes");
}
}
Random r = new Random();
public MainViewModel()
{
//hard coded data for testing
VNodes = new ObservableCollection<VNode>();
List<string> names = new List<string>() { "Tammy", "Doug", "Jeff", "Greg", "Kris", "Mike", "Joey", "Leslie", "Emily","Tom" };
List<int> ages = new List<int>() { 32, 24, 42, 57, 17, 73, 12, 8, 29, 31 };
for (int i = 0; i < 10; i++)
{
VNode item = new VNode();
int x = r.Next(0,9);
item.Name = names[x];
item.Age = ages[x];
item.Kids = r.Next(1, 5);
VNodes.Add(item);
}
}
}
}
ObservableObject.cs
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApplication1
{
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
UPDATED
For the sake of example, how about demonstrating if the user just selects a single item in the ListBox on the right, it then displays that selected items more in-depth data on the right as shown in the image below?
There are three and a half answers here. Number one is good general WPF practice that doesn't work in the specific case of ListBox. The second one is a quick and dirty workaround for the problem with ListBox, and the last is the best, because it does nothing in code behind. Least code behind is best code behind.
The first way to do this doesn't require anything of the items you're displaying in the ListBox. They could be strings or integers. If your item type (or types) is a class (or are classes) with a little more meat to it, and you'd like to have each instance know whether it's been selected or not, we'll get to that next.
You need to give your view model another ObservableCollection<VNode> called SelectedVNodes or some such.
private ObservableCollection<VNode> _selectedvnodes;
public ObservableCollection<VNode> SelectedVNodes
{
get { return _selectedvnodes; }
set
{
_selectedvnodes = value;
NotifyPropertyChanged("SelectedVNodes");
}
}
public MainViewModel()
{
VNodes = new ObservableCollection<VNode>();
SelectedVNodes = new ObservableCollection<VNode>();
// ...etc., just as you have it now.
If System.Windows.Controls.ListBox weren't broken, then in your first ListBox, you would bind SelectedItems to that viewmodel property:
<ListBox
Grid.Column="0"
Background="AliceBlue"
ItemsSource="{Binding VNodes}"
SelectedItems="{Binding SelectedVNodes}"
SelectionMode="Extended">
And the control would be in charge of the content of SelectedVNodes. You could also change SelectedVNodes programmatically, and that would update both lists.
But System.Windows.Controls.ListBox is broken, and you can't bind anything to SelectedItems. The simplest workaround is to handle the ListBox's SelectionChanged event and kludge it in the code behind:
XAML:
<ListBox
Grid.Column="0"
Background="AliceBlue"
ItemsSource="{Binding VNodes}"
SelectionMode="Extended"
SelectionChanged="ListBox_SelectionChanged">
C#:
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox lb = sender as ListBox;
MainViewModel vm = DataContext as MainViewModel;
vm.SelectedVNodes.Clear();
foreach (VNode item in lb.SelectedItems)
{
vm.SelectedVNodes.Add(item);
}
}
Then bind ItemsSource in your second ListBox to SelectedVNodes:
<ListBox
Grid.Column="2"
Background="LightBlue"
ItemsSource="{Binding SelectedVNodes}">
And that should do what you want. If you want to be able to update SelectedVNodes programmatically and have the changes reflected in both lists, you'll have to have your codebehind class handle the PropertyChanged event on the viewmodel (set that up in the codebehind's DataContextChanged event), and the CollectionChanged event on viewmodel.SelectedVNodes -- and remember to set the CollectionChanged handler all over again every time SelectedVNodes changes its own value. It gets ugly.
A better long-term solution would be to write an attachment property for ListBox that replaces SelectedItems and works right. But this kludge will at least get you moving for the time being.
Update
Here's a second way of doing it, which OP suggested. Instead of maintaining a selected item collection, we put a flag on each item, and the viewmodel has a filtered version of the main item list that returns only selected items. I'm drawing a blank on how to bind VNode.IsSelected to the IsSelected property on ListBoxItem, so I just did that in the code behind.
VNode.cs:
using System;
namespace WpfApplication1
{
public class VNode
{
public string Name { get; set; }
public int Age { get; set; }
public int Kids { get; set; }
// A more beautiful way to do this would be to write an IVNodeParent
// interface with a single method that its children would call
// when their IsSelected property changed -- thus parents would
// implement that, and they could name their "selected children"
// collection properties anything they like.
public ObservableObject Parent { get; set; }
private bool _isSelected = false;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value != _isSelected)
{
_isSelected = value;
if (null == Parent)
{
throw new NullReferenceException("VNode.Parent must not be null");
}
Parent.NotifyPropertyChanged("SelectedVNodes");
}
}
}
}
}
MainViewModel.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApplication1
{
public class MainViewModel : ObservableObject
{
private ObservableCollection<VNode> _vnodes;
public ObservableCollection<VNode> VNodes
{
get { return _vnodes; }
set
{
_vnodes = value;
NotifyPropertyChanged("VNodes");
NotifyPropertyChanged("SelectedVNodes");
}
}
public IEnumerable<VNode> SelectedVNodes
{
get { return _vnodes.Where(vn => vn.IsSelected); }
}
Random r = new Random();
public MainViewModel()
{
//hard coded data for testing
VNodes = new ObservableCollection<VNode>();
List<string> names = new List<string>() { "Tammy", "Doug", "Jeff", "Greg", "Kris", "Mike", "Joey", "Leslie", "Emily","Tom" };
List<int> ages = new List<int>() { 32, 24, 42, 57, 17, 73, 12, 8, 29, 31 };
for (int i = 0; i < 10; i++)
{
VNode item = new VNode();
int x = r.Next(0,9);
item.Name = names[x];
item.Age = ages[x];
item.Kids = r.Next(1, 5);
item.Parent = this;
VNodes.Add(item);
}
}
}
}
MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (VNode item in e.RemovedItems)
{
item.IsSelected = false;
}
foreach (VNode item in e.AddedItems)
{
item.IsSelected = true;
}
}
}
}
MainWindow.xaml (partial):
<ListBox
Grid.Column="0"
Background="AliceBlue"
ItemsSource="{Binding VNodes}"
SelectionMode="Extended"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<ListBox Grid.Column="2" Background="LightBlue" ItemsSource="{Binding SelectedVNodes}">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text=":" FontWeight="Bold" />
<TextBlock Text=" age:"/>
<TextBlock Text="{Binding Age}" FontWeight="Bold" />
<TextBlock Text=" kids:"/>
<TextBlock Text="{Binding Kids}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Update 2
And here, finally, is how you do it with binding (thanks to OP for figuring out for me how to bind data item properties to ListBoxItem properties -- I should be able to accept his comment as an answer!):
In MainWindow.xaml, get rid of the SelectionCanged event (yay!), and set a Style to do the binding only on the items in the first ListBox. In the second ListBox, that binding will create problems which I'll leave to somebody else to resolve; I have a guess that it might be fixable by fiddling with the order of notifications and assignments in VNode.IsSelected.set, but I could be wildly wrong about that. Anyway the binding serves no purpose in the second ListBox so there's no reason to have it there.
<ListBox
Grid.Column="0"
Background="AliceBlue"
ItemsSource="{Binding VNodes}"
SelectionMode="Extended"
>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
...and I removed the event handler method from the codebehind. But you didn't add it at all, because you're smarter than me and you started with this last version of the answer.
In VNode.cs, VNode becomes an ObservableObject so he can advertise his selection status, and he also fires the appropriate notification in IsSelected.set. He still has to fire the change notification for his Parent's SelectedVNodes property, because the second listbox (or any other consumer of SelectedVNodes) needs to know that the set of selected VNodes has changed.
Another way to do that would be to make SelectedVNodes an ObservableCollection again, and have VNode add/remove himself from it when his selected status changes. Then the viewmodel would have to handle CollectionChanged events on that collection, and update the VNode IsSelected properties when they're added to it or removed from it. If you do that, it's very important to keep the if in VNode.IsSelected.set, to prevent infinite recursion.
using System;
namespace WpfApplication1
{
public class VNode : ObservableObject
{
public string Name { get; set; }
public int Age { get; set; }
public int Kids { get; set; }
public ObservableObject Parent { get; set; }
private bool _isSelected = false;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (value != _isSelected)
{
_isSelected = value;
if (null == Parent)
{
throw new NullReferenceException("VNode.Parent must not be null");
}
Parent.NotifyPropertyChanged("SelectedVNodes");
NotifyPropertyChanged("IsSelected");
}
}
}
}
}
Update 3
OP asks about displaying a single selection in a detail pane. I left the old multi-detail pane in place to demonstrate sharing a template.
That's pretty simple to do, so I elaborated a bit. You could do this only in the XAML, but I threw in a SelectedVNode property in the viewmodel to demonstrate that as well. It's not used for anything, but if you wanted to throw in a command that operated on the selected item (for example), that's how the view model would know which item the user means.
MainViewModel.cs
// Add to MainViewModle class
private VNode _selectedVNode = null;
public VNode SelectedVNode
{
get { return _selectedVNode; }
set
{
if (value != _selectedVNode)
{
_selectedVNode = value;
NotifyPropertyChanged("SelectedVNode");
}
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="ListBackgroundBrush" Color="Ivory" />
<DataTemplate x:Key="VNodeCardTemplate">
<Grid>
<Border
x:Name="BackgroundBorder"
BorderThickness="1"
BorderBrush="Silver"
CornerRadius="16,6,6,6"
Background="White"
Padding="6"
Margin="4,4,8,8"
>
<Border.Effect>
<DropShadowEffect BlurRadius="2" Opacity="0.25" ShadowDepth="4" />
</Border.Effect>
<Grid
x:Name="ContentGrid"
>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<!-- Each gets half of what's left -->
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Border
Grid.Row="0" Grid.RowSpan="3"
VerticalAlignment="Top"
Grid.Column="0"
BorderBrush="{Binding Path=BorderBrush, ElementName=BackgroundBorder}"
BorderThickness="1"
CornerRadius="9,4,4,4"
Margin="2,2,6,2"
Padding="4"
>
<StackPanel Orientation="Vertical">
<StackPanel.Effect>
<DropShadowEffect BlurRadius="2" Opacity="0.25" ShadowDepth="2" />
</StackPanel.Effect>
<Ellipse
Width="16" Height="16"
Fill="DarkOliveGreen"
Margin="0,0,0,2"
HorizontalAlignment="Center"
/>
<Border
CornerRadius="6,6,2,2"
Background="DarkOliveGreen"
Width="36"
Height="18"
Margin="0"
/>
</StackPanel>
</Border>
<TextBlock Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding Name}" FontWeight="Bold" />
<Separator Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Background="{Binding Path=BorderBrush, ElementName=BackgroundBorder}" Margin="0,3,0,3" />
<!--
Mode=OneWay on Run.Text because bindings on that property should default to that, but don't.
And if you bind TwoWay to a property without a setter, it throws an exception.
-->
<TextBlock Grid.Row="2" Grid.Column="1"><Bold>Age:</Bold> <Run Text="{Binding Age, Mode=OneWay}" /></TextBlock>
<TextBlock Grid.Row="2" Grid.Column="2"><Bold>Kids:</Bold> <Run Text="{Binding Kids, Mode=OneWay}" /></TextBlock>
</Grid>
</Border>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter TargetName="ContentGrid" Property="Visibility" Value="Hidden" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<!-- I think this should be the default, but it isn't. -->
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</Window.Resources>
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="8" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="0.5*" />
</Grid.RowDefinitions>
<ListBox
x:Name="VNodeMasterList"
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Background="{StaticResource ListBackgroundBrush}"
ItemsSource="{Binding VNodes}"
SelectionMode="Extended"
SelectedItem="{Binding SelectedVNode}"
>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<GridSplitter Grid.Column="1" Grid.RowSpan="2" Grid.Row="0" Width="5" HorizontalAlignment="Stretch" />
<Border
Grid.Column="2"
Grid.Row="0"
Background="{StaticResource ListBackgroundBrush}"
>
<ContentControl
Content="{Binding ElementName=VNodeMasterList, Path=SelectedItem}"
ContentTemplate="{StaticResource VNodeCardTemplate}"
/>
</Border>
<ListBox
Grid.Column="2"
Grid.Row="1"
Background="{StaticResource ListBackgroundBrush}"
ItemsSource="{Binding SelectedVNodes}"
ItemTemplate="{StaticResource VNodeCardTemplate}"
/>
</Grid>
</Window>
I have a need to use two listboxes, each bound to a different collection.
i originally had this working with one listbox and binding before the need to bind two came up.
Here is how I was doing that.
<Window x:Class="TeamManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc ="clr-namespace:TeamManager"
Title="Game Manager" Height="800" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type loc:Game}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Name="dateBlock" Grid.Column="0" Grid.Row="1" Text="{Binding Date, StringFormat=d}"></TextBlock>
<TextBlock Name="TimeBlock" Grid.Column="1" Grid.Row="1" Text="{Binding Time}"></TextBlock>
<Button Grid.Row="1" Grid.Column="2" CommandParameter="{Binding Id}" Click="Manage_Click" >Manage</Button>
<Button Grid.Row="1" Grid.Column="3" CommandParameter="{Binding Id}" Click="Delete_Click" Height="16" Width="16">
<Image Source="/Images/DeleteRed.png"></Image>
</Button>
</Grid>
</DataTemplate>
</Window.Resources>
<StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<StackPanel>
<TextBlock>Upcomming Games</TextBlock>
<ListBox ItemsSource="{Binding}" Name="GameList"></ListBox>
</StackPanel>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left">
<Button Height="30" Width="100" Margin="10,10,10,10" Click="AddGame_Click">Add New Game</Button>
</StackPanel>
</StackPanel>
</StackPanel>
And my code simply set the DataContext of the window to a ObservableCollection
with the need to use TWO collections I created a wrapper class like this
public class AppModel
{
public ObservableCollection<Game> gameCollection { get; set; }
public ObservableCollection<Player> playerCollection { get; set; }
}
And my CS is now setting the DataContext to an object of AppModel
GameDBEntities _entity = new GameDBEntities();
AppModel _model;
public MainWindow()
{
InitializeComponent();
DataContext = model;
}
AppModel model
{
get
{
if (_model == null)
{
_model = new AppModel();
}
if (_model.gameCollection == null)
{
_model.gameCollection = new ObservableCollection<Game>(_entity.Games);
}
if (_model.playerCollection == null)
{
_model.playerCollection = new ObservableCollection<Player>(_entity.Players);
}
return _model;
}
set { }
}
In my Xaml, how can I set the datacontext of the existing listBox to be bound to the Collection Of Games in The AppModel?
Once I get that working I will work on the second listbox on my own.
Thanks!
You need to add a Path to the Binding. The DatacContext will be the model, the path should point to either collection:
<ListBox ItemsSource="{Binding gameCollection}" ...
Would changing the Binding to <ListBox ItemsSource="{Binding Path=gameCollection}" Name="GameList"></ListBox> solve your problem?
As per your question you state that you used to set the DataContext to the gameCollection, but now that you have changed this to use the AppModel, you will need to also change your binding as appropriate.
This will essentially change the Binding from being just bound to gameCollection, it will now be set to use AppData.gameCollection.